外联有什么作用

What does extern inline do?

本文关键字:作用 什么      更新时间:2023-10-16

我知道inline本身就是对编译器的建议,它可以自行决定内联函数,也可以不内联函数,并且它还会产生可链接的目标代码。

我认为static inline做同样的事情(可能是内联的,也可能不是内联的),但在内联时不会产生可链接的目标代码(因为没有其他模块可以链接到它)。

extern inline在图片中的位置如何?

假设我想用内联函数替换预处理器宏,并要求该函数内联(例如,因为它使用 __FILE____LINE__宏,这些宏应该为调用者解析,但不是这个被调用的函数)。也就是说,我希望看到编译器或链接器错误,以防函数未内联。extern inline这样做吗?(我假设,如果没有,除了坚持使用宏之外,没有办法实现这种行为。

C++ 和 C 之间有区别吗?

不同的编译器供应商和版本之间是否存在差异?

在K&R C或C89中,内联不是语言的一部分。许多编译器将其实现为扩展,但没有关于它如何工作的定义语义。GCC 是最早实现内联的公司之一,并引入了inlinestatic inlineextern inline结构;大多数 C99 之前的编译器通常遵循它的领导。

GNU89:

  • inline :该函数可能是内联的(虽然这只是一个提示)。始终发出出线外版本并在外部可见。因此,您只能在一个编译单元中定义这样的内联,并且其他每个编译单元都需要将其视为外联函数(否则您将在链接时获得重复的符号)。
  • extern inline不会生成外联版本,但可能会调用一个(因此您必须在其他编译单元中定义该版本。但是,单定义规则适用;外联版本必须具有与此处提供的内联版本相同的代码,以防编译器调用它。
  • static inline 不会生成外部可见的外联版本,尽管它可能会生成文件静态版本。单定义规则不适用,因为永远不会发出外部符号,也不会调用外部符号。

C99(或 GNU99):

  • inline:像GNU89的"extern inline";没有发出外部可见的函数,但可以调用一个,所以必须存在
  • extern inline:像GNU89"内联":发出外部可见的代码,所以最多一个翻译单元可以使用它。
  • static inline:像GNU89"静态内联"。这是 gnu89 和 c99 之间唯一的便携式

C++:

在任何地方内联的

函数必须在任何地方内联,具有相同的定义。编译器/链接器将整理符号的多个实例。没有定义static inlineextern inline,尽管许多编译器都有它们(通常遵循gnu89模型)。

我相信你误解了__FILE__,并根据这个陈述__LINE__:

因为它使用 __FILE__ 和 __LINE__宏,这些宏应该为调用方解析,但不是这个调用 功能

编译有几个阶段,预处理是第一个阶段。 在该阶段更换__FILE__和__LINE__。 因此,当编译器可以考虑内联函数时,它们已经被替换了。

听起来你正在尝试写这样的东西:

inline void printLocation()
{
  cout <<"You're at " __FILE__ ", line number" __LINE__;
}
{
...
  printLocation();
...
  printLocation();
...
  printLocation();

并希望您每次都能打印不同的值。正如 Don 所说,你不会,因为__FILE__和__LINE__是由预处理器实现的,但内联是由编译器实现的。因此,无论您从哪里调用printLocation,您都会得到相同的结果。

使

它工作的唯一方法是使 printLocation 成为宏。(是的,我知道...

#define PRINT_LOCATION  {cout <<"You're at " __FILE__ ", line number" __LINE__}
...
  PRINT_LOCATION;
...
  PRINT_LOCATION;
...

我不是回答"它做什么?",而是回答"我如何让它做我想做的事?" 有 5 种内联,在 GNU C89、标准 C99 和 C++ 中都有。MSVC有一些(请注意,我还没有测试过MSVC代码)

始终内联,除非地址被占用

__attribute__((always_inline))添加到任何声明中,然后使用其中一个以下案例,以处理其地址被占用的可能性。

你可能永远不应该使用它,除非你需要它的语义(例如,以某种方式影响程序集,或使用alloca)。编译器通常比你更清楚它是否值得。

MSVC 具有看起来基本相同的__forceinline,但显然它拒绝在其他编译器管理得很好的很多常见情况下(例如,当优化关闭时)内联。

内联并发出一个弱符号(如C++,又名"只是让它工作")

__attribute__((weak))
void foo(void);
inline void foo(void) { ... }

请注意,这会留下一堆相同代码的副本,链接器会任意选择一个。

MSVC 在 C 模式下似乎没有完全相同的等价物,尽管有一些类似的东西。 __declspec(selectany)似乎只在谈论数据,所以可能不适用于函数?还有对弱别名的链接器支持,但这在这里有效吗?

内联,但从不发出任何符号(留下外部引用)

__attribute__((gnu_inline))
extern inline void foo(void) { ... }

MSVC的__declspec(dllimport),结合实际定义(否则不寻常),据说可以做到这一点。

始终发出(对于一个 TU,以解决前面的问题)

暗示版本在C++中发出弱符号,但在 C 的任一方言中发出强符号:

void foo(void);
inline void foo(void) { ... }

或者你可以在没有提示的情况下做到这一点,它会在两种语言中发出一个强烈的符号:

void foo(void) { ... }

通常,您在提供定义时知道 TU 是什么语言,并且可能不需要太多内联。

MSVC的__declspec(dllexport)据说是这样做的。

内联并在每个 TU 中发射

static inline void foo(void) { ... }
<小时 />

对于除static之外的所有这些,您可以在上面添加void foo(void)声明。这有助于编写干净标头的"最佳实践",然后#include具有内联定义的单独文件。然后,如果使用 C 样式内联,则在一个专用 TU 中以不同的方式#define一些宏以提供外联定义。

不要忘记extern "C"标头是否可以同时从 C 和 C++ 使用!

<小时 />

还有一些相关的事情:

从不内联

__attribute__((noinline))添加到函数的任何声明中。

MSVC 具有__declspec(noinline)[[msvc::noinline]],但据记录,它仅适用于成员功能。但是,我已经看到提到可能会阻止内联的"安全属性"?

如果可能,强制将其他函数内联到此函数中。

__attribute__((flatten))添加到函数的任何声明中。

请注意,noinline比这更强大(未在 MSVC 上测试),在编译时定义未知的函数也是如此。

MSVC 现在记录[[msvc::flatten]];请注意,它适用于作用域而不是函数。以前没有等价物,因为[[msvc::forceinline_calls]]不是递归的。

内联、静态内联和外联的情况很复杂,尤其是因为 gcc 和 C99 为它们的行为定义了略有不同的含义(大概也是C++)。 你可以在这里找到一些关于他们在 C 中做什么的有用和详细的信息。

在这里您可以选择宏,而不是内联函数。宏统治内联函数的罕见情况。尝试以下操作:我编写了这个"宏魔法"代码,它应该可以工作!在 gcc/g++ Ubuntu 10.04 上测试

//(c) 2012 enthusiasticgeek (LOGGING example for StackOverflow)
#ifdef __cplusplus
#include <cstdio>
#include <cstring>
#else
#include <stdio.h>
#include <string.h>
#endif
//=========== MACRO MAGIC BEGINS ============
//Trim full file path
#define __SFILE__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/')+1 : __FILE__ )
#define STRINGIFY_N(x) #x
#define TOSTRING_N(x) STRINGIFY_N(x)
#define _LINE (TOSTRING_N(__LINE__))
#define LOG(x, s...) printf("(%s:%s:%s)"  x "n" , __SFILE__, __func__, _LINE, ## s);
//=========== MACRO MAGIC ENDS ============
int main (int argc, char** argv) {
  LOG("Greetings StackOverflow! - from enthusiasticgeekn");
  return 0;
}

对于多个文件,在单独的头文件中定义这些宏,包括每个 c/cc/cxx/cpp 文件中的相同宏。请尽可能首选内联函数或常量标识符(根据情况需要)而不是宏。

仅限C++:

正如其他人所指出的,宏(此处为__FILE____LINE__)在编译和链接之前进行评估;因此,如果您有一个使用这些函数并且您希望它们对每个文件都不同,则需要与 inline 相反。由于每个文件的__FILE__值和__LINE__值将不同,因此每个文件的函数定义(主体)将不同。但是(非静态)inline意味着如果函数在多个翻译单元中定义,则它们都必须具有相同的定义。

您可以在头文件中定义(而不是声明)普通函数或staticstatic inline函数,并将其包含在所需的任何位置。这样,每个翻译单元(源文件)都会获得自己的函数副本,具有不同的__FILE____LINE__值。虽然,我认为在static inline的情况下,inline关键字在大多数情况下是无用的。