外部内联

时间:2008-10-19 15:09:28

标签: c++ c extern inline-functions

我理解“内联”本身就是对编译器的建议,并且在它的结构中它可能会也可能不会内联函数,并且它也会产生可链接的目标代码。

我认为“静态内联”执行相同的操作(可能内联也可能不内联)但内联时不会生成可链接的目标代码(因为没有其他模块可以链接到它)。

“extern inline”在哪里适合图片?

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

C ++和C之间是否存在差异?

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

6 个答案:

答案 0 :(得分:122)

在K& R C或C89中,内联不是该语言的一部分。许多编译器将其实现为扩展,但没有关于它如何工作的定义语义。 GCC是最早实施内联的人之一,并介绍了inlinestatic inlineextern inline结构;大多数前C99编译器一般都遵循其领先优势。

GNU89:

  • 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模型)。

答案 1 :(得分:29)

我相信你会根据这句话误解__FILE__和__LINE__:

  

因为它使用__FILE__和   __LINE__宏应该为调用者解析但不会调用此调用   功能

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

答案 2 :(得分:12)

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

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;
...

答案 3 :(得分:3)

内联,静态内联和外部内联的情况很复杂,尤其是因为gcc和C99的行为定义略有不同(也可能是C ++)。您可以在C here中找到一些有用且详细的信息。

答案 4 :(得分:2)

宏是您的选择,而不是内联函数。一种罕见的情况,其中宏规则内联函数。尝试以下内容:我写了这个“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文件中的相同。请优先选择内联函数或const标识符(视情况而定)而不是宏尽可能。

答案 5 :(得分:0)

我不是在回答“它做什么?”,而是在回答“我如何使它做我想要的事情?”内联共有5种,在GNU C89,标准C99和C ++中都可用:

始终内联,除非地址已使用

在任何声明中添加__attribute__((always_inline)),然后使用其中之一 在以下情况下可以解决其地址被盗的可能性。

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

内联并发出一个弱符号(例如C ++,也就是“使其正常工作”)

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

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

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

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

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

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

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

或者您可以在没有提示的情况下执行此操作,提示会在两种语言中发出强烈的符号:

void foo(void) { ... }

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

内联并在每个TU中发出

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

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

如果C和C ++都可以使用标头,请不要忘记std::set