繁体   English   中英

外部内联做什么?

[英]What does extern inline do?

我知道inline本身是对编译器的建议,它可能会或可能不会内联 function,它还会生成可链接的 object 代码。

我认为static inline执行相同的操作(可能内联也可能不内联),但内联时不会产生可链接的 object 代码(因为没有其他模块可以链接到它)。

extern inline在哪里适合图片?

假设我想用内联 function 替换预处理器宏,并要求这个 function 被内联(例如,因为它使用__FILE____LINE__宏,它们应该为调用者而不是这个被调用函数解析)。 也就是说,如果 function 没有内联,我想查看编译器或 linker 错误。 extern inline会这样做吗? (我认为,如果没有,除了坚持使用宏之外,没有其他方法可以实现这种行为。)

C++和C有区别吗?

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

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

GNU89:

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

C99(或 GNU99):

  • inline : 像 GNU89 "extern inline"; 不发出任何外部可见的函数,但可能会被调用,因此必须存在
  • extern inline : 就像 GNU89 "inline": 发出外部可见的代码,所以最多一个翻译单元可以使用它。
  • 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();

并希望您每次都能打印出不同的值。 正如唐所说,你不会,因为 __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一些不同的宏以提供外联定义。

如果头文件可以从 C 和 C++ 中使用,请不要忘记extern "C"


还有一些相关的事情:

从不内联

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

MSVC 有__declspec(noinline)但它被记录为仅适用于成员函数。 但是,我已经看到提到可能会阻止内联的“安全属性”?

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

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

请注意, noinline比这更强大,其定义在编译时未知的函数也是如此。

MSVC 似乎没有等价物。 我见过一次提到[[msvc::forceinline_calls]] (应用于语句或块),但它不是递归的。

宏是您的选择,而不是内联函数。 宏统治内联函数的罕见情况。 尝试以下操作:我写了这个“MACRO MAGIC”代码,它应该可以工作! 在 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 enthusiasticgeek\n");

  return 0;
}

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

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

仅限 C++:

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

You could define (not declare) a normal function or static or static inline function in a header file and include it anywhere you want. 这样,每个翻译单元(源文件)都会获得自己的 function 副本,其中__FILE____LINE__值不同。 虽然,我认为在static inline的情况下, inline关键字在大多数情况下是无用的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM