extern内联有什么作用?


93

我知道,这inline本身就是对编译器的建议,它可以酌情决定是否内联函数,也可以生成可链接的目标代码。

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

哪里extern inline适合图片?

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

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

不同的编译器供应商和版本之间有区别吗?

Answers:


129

在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 inline或的定义没有定义extern inline,尽管许多编译器都有定义(通常遵循gnu89模型)。


2
在经典经典C中,“内联”不是关键字;它可用作变量名。这将适用于C89和预标准(K&R)C.
乔纳森莱弗勒

您说得对,看来。固定。我以为它已在C89中保留为关键字(尽管不是在K&R中),但是我似乎记错了
puetzk

我想补充一点,对于Microsoft的Visual C ++,有一个__forceinline关键字,它将强制您的函数被内联。显然,这是仅针对VC ++的编译器特定的扩展。
无题

C99“外部内联”和根本没有说明符之间有什么区别吗?
乔·苏

语义上不;就像非内联函数一样,extern inline它受一个定义规则的约束,这就是定义。但是,如果实现定义的优化启发式方法遵循建议使用inline关键字作为建议,即“尽可能快地调用函数”(ISO 9899:1999§6.7.4(5),则extern inline算是
puetzk

31

我相信您根据以下陈述误解了__FILE__和__LINE__:

因为它使用__FILE__和__LINE__宏,这些宏应为调用者解析,但不能为调用的函数解析

编译有多个阶段,而预处理是第一个阶段。在该阶段将替换__FILE__和__LINE__。因此,到编译器可以考虑用于内联的函数时,它们已被替换。


14

听起来您正在尝试编写如下内容:

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

18
一个常见的技巧是让宏PRINT_LOCATION调用函数printLocation,并将FILELINE作为参数传递。当函数主体不平凡时,这可以导致更好的调试器/编辑器/等行为。
史蒂夫·杰索普

@Roddy看看我的解决方案-您的扩展,但更全面和可扩展。
狂热爱好者

@SteveJessop在下面的解决方案中列出了类似的内容吗?
狂热爱好者

3

内联,静态内联和外部内联的情况非常复杂,这不仅是因为gcc和C99为它们的行为(也可能是C ++)定义了稍有不同的含义。您可以在此处找到有关它们在C语言中执行的操作的有用且详细的信息。


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标识符(视情况需要)而不是宏。


2

我不是在回答“它做什么?”,而是在回答“我如何使它做我想要的?”。内联共有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使用内联定义创建一个单独的文件。然后,如果使用C风格的内联,#define一个专用TU中的某些宏会有所不同,以提供脱机定义。

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


丢失:MSVC呢?它具有一些C89的方言扩展名,但是我从不使用MSVC,也不知道如何运行其nm等效语言。
o11c
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.