我了解 inline
本身是对编译器的建议,它可能会或可能不会内联函数,并且它还会生成可链接的目标代码。
我认为 static inline
做同样的事情(可能内联也可能不内联)但在内联时不会产生可链接的目标代码(因为没有其他模块可以链接到它)。
extern inline
在图片中的什么位置?
假设我想用一个内联函数替换一个预处理器宏,并要求这个函数被内联(例如,因为它使用了__FILE__
和 __LINE__
宏,它们应该为调用者解析,而不是这个被调用函数)。也就是说,如果函数没有内联,我想查看编译器或链接器错误。 extern inline
会这样做吗? (我认为,如果没有,除了坚持使用宏之外,没有其他方法可以实现这种行为。)
C++ 和 C 之间有区别吗?
不同的编译器供应商和版本之间是否存在差异?
在 K&RC 或 C89 中,内联不是语言的一部分。许多编译器将其作为扩展实现,但没有定义关于它如何工作的语义。 GCC 是最早实现内联的,并引入了 inline
、static inline
和 extern inline
结构;大多数 C99 之前的编译器通常会效仿。
GNU89:
内联:该函数可能是内联的(虽然这只是一个提示)。离线版本始终会发出并在外部可见。因此,您只能在一个编译单元中定义这样的内联,而其他每个编译单元都需要将其视为一个外联函数(否则您将在链接时获得重复的符号)。
extern inline 不会生成外联版本,但可能会调用一个(因此您必须在其他编译单元中定义它。但是,单定义规则适用;外联版本必须具有相同的代码作为这里提供的内联,以防编译器调用它。
static inline 不会生成外部可见的离线版本,尽管它可能会生成文件静态版本。单一定义规则不适用,因为从来没有发出外部符号,也没有调用一个。
C99(或 GNU99):
内联:像 GNU89 “外部内联”;没有发出外部可见的函数,但可能会调用一个函数,因此必须存在
extern inline:像 GNU89 “inline”:发出外部可见的代码,所以最多一个翻译单元可以使用它。
静态内联:像 GNU89 “静态内联”。这是gnu89和c99之间唯一的便携式
C++:
在任何地方都内联的函数必须在任何地方都内联,并且具有相同的定义。编译器/链接器将整理符号的多个实例。没有 static inline
或 extern 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__ 是由预处理器实现的,但 inline 是由编译器实现的。因此,无论您从哪里调用 printLocation,都会得到相同的结果。
你可以让它工作的唯一方法是让 printLocation 成为一个宏。 (是的,我知道...)
#define PRINT_LOCATION {cout <<"You're at " __FILE__ ", line number" __LINE__}
...
PRINT_LOCATION;
...
PRINT_LOCATION;
...
宏是您的选择,而不是内联函数。宏统治内联函数的罕见情况。请尝试以下操作:我编写了这个“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 种内联,在 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)
似乎只在谈论数据,所以可能不适用于函数?还有对 weak aliases 的链接器支持,但这在这里有效吗?
内联,但从不发出任何符号(留下外部引用)
__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 风格的内联,#define
在一个专用 TU 中以不同的方式使用一些宏来提供外联定义。
如果 C 和 C++ 都可以使用标头,请不要忘记 extern "C"
!
还有一些相关的事情:
从不内联
将 __attribute__((noinline))
添加到函数的任何声明中。
MSVC 有 __declspec(noinline)
,但它被记录为仅适用于成员函数。但是,我看到提到可能会阻止内联的“安全属性”?
如果可能,强制将其他函数内联到这个函数中。
将 __attribute__((flatten))
添加到函数的任何声明中。
请注意,noinline
比这更强大,其定义在编译时未知的函数也是如此。
MSVC 似乎没有等价物。我见过一次提到 [[msvc::forceinline_calls]]
(应用于语句或块),但它不是递归的。
nm
等效项。
void foo(void); inline void foo(void) { ... }
的标题是“总是发射(对于一个 TU,解决前面的问题)”。其他 4 个以“inline ....”开头。在情况 4:它是否也 inline ?
__attribute__((always_inline))
。即使禁用了优化,它也会强制内联函数。它对极其频繁地调用的极短函数很有用,例如在向量数学库中,如果它们没有内联,则在优化关闭时调试运行缓慢。