我知道inline
本身就是对编译器的建议,它可以自行决定内联函数,也可以不内联函数,并且它还会产生可链接的目标代码。
我认为static inline
做同样的事情(可能是内联的,也可能不是内联的),但在内联时不会产生可链接的目标代码(因为没有其他模块可以链接到它)。
extern inline
在图片中的位置如何?
假设我想用内联函数替换预处理器宏,并要求该函数内联(例如,因为它使用 __FILE__
和__LINE__
宏,这些宏应该为调用者解析,但不是这个被调用的函数)。也就是说,我希望看到编译器或链接器错误,以防函数未内联。extern inline
这样做吗?(我假设,如果没有,除了坚持使用宏之外,没有办法实现这种行为。
C++ 和 C 之间有区别吗?
不同的编译器供应商和版本之间是否存在差异?
在K&R C或C89中,内联不是语言的一部分。许多编译器将其实现为扩展,但没有关于它如何工作的定义语义。GCC 是最早实现内联的公司之一,并引入了inline
、static inline
和extern inline
结构;大多数 C99 之前的编译器通常遵循它的领导。
GNU89:
-
inline
:该函数可能是内联的(虽然这只是一个提示)。始终发出出线外版本并在外部可见。因此,您只能在一个编译单元中定义这样的内联,并且其他每个编译单元都需要将其视为外联函数(否则您将在链接时获得重复的符号)。 -
extern inline
不会生成外联版本,但可能会调用一个(因此您必须在其他编译单元中定义该版本。但是,单定义规则适用;外联版本必须具有与此处提供的内联版本相同的代码,以防编译器调用它。 -
static inline
不会生成外部可见的外联版本,尽管它可能会生成文件静态版本。单定义规则不适用,因为永远不会发出外部符号,也不会调用外部符号。
C99(或 GNU99):
-
inline
:像GNU89的"extern inline";没有发出外部可见的函数,但可以调用一个,所以必须存在 -
extern inline
:像GNU89"内联":发出外部可见的代码,所以最多一个翻译单元可以使用它。 -
static 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__是由预处理器实现的,但内联是由编译器实现的。因此,无论您从哪里调用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
意味着如果函数在多个翻译单元中定义,则它们都必须具有相同的定义。
您可以在头文件中定义(而不是声明)普通函数或static
或static inline
函数,并将其包含在所需的任何位置。这样,每个翻译单元(源文件)都会获得自己的函数副本,具有不同的__FILE__
和__LINE__
值。虽然,我认为在static inline
的情况下,inline
关键字在大多数情况下是无用的。