我知道内联函数要么在被调用的地方被替换,要么表现为普通函数。
但是我怎么知道内联函数是否在它被调用的地方实际被替换,因为在编译时将内联函数视为内联的决定?
在运行时以编程方式,您不能。
事情的真相是: 你不需要知道
编译器可以选择inline
未标记的inline
函数或忽略显式标记的函数inline
,这完全是编译器的愿望(阅读智慧)&您应该相信编译器会明智地完成其工作。大多数主流编译器都会很好地完成他们的工作。
如果您的问题纯粹是从学术角度来看,那么有几种选择:
您可以检查汇编代码以检查函数代码是否在调用点内联。
如何生成汇编代码?
对于 gcc:
编译时
使用-S
开关。
例如:
g++ -S FileName.cpp
生成的汇编代码被创建为 file FileName.s
。
对于 MSVC:从命令行
使用/FA 开关。
在生成的汇编代码中查找是否有call
特定函数的汇编指令。
如果某些编译器未能遵守内联函数请求,它们会发出警告。
例如,在 gcc 中,-Winline
如果编译器没有内联声明为内联的函数,命令选项将发出警告。
查看GCC 文档以获取更多详细信息:
-Winline
如果声明为 inline 的函数不能被内联,则发出警告。即使使用此选项,编译器也不会警告系统头文件中声明的内联函数失败。
编译器使用各种启发式方法来确定是否内联函数。例如,编译器会考虑被内联的函数的大小以及在当前函数中已经完成的内联量。因此,源程序中看似微不足道的变化,都会导致产生的警告
-Winline
出现或消失。
检查生成的代码。如果函数被展开,你会看到它的主体,而不是一个call
或类似的指令。
您可以使用工具列出目标文件中的符号,例如nm
在 Linux 上。如果函数是内联的,它将不会在nm
输出中列出 - 它成为其他函数的一部分。此外,您将无法在调试器中按名称在此函数上放置断点。
如果您需要确保该函数是内联的并且可以与 MS VC++ 中的专有扩展一起使用,请查看__forceinline
declarator。编译器将内联该函数,或者,如果它属于记录的特殊情况列表,您将收到警告 - 这样您就会知道内联状态。
不以任何方式认可它。
使用gdb,如果你不能调用一个函数,它可能的含义之一就是函数是内联的。反过来推理,如果可以在gdb内部调用函数,则意味着该函数未标记为内联。
是否内联函数由编译器决定。而且由于它是由编译器制作的,所以是的,它只能在编译时制作。
因此,如果您可以使用 -S 选项查看汇编代码(使用 gcc -S 生成汇编代码),您可以查看您的函数是否已内联。
有一种方法可以确定函数是否以编程方式内联,而无需查看汇编代码。这个答案取自这里。
假设您要检查特定呼叫是否内联。你会这样。编译器内联函数,但对于那些导出的函数(并且几乎所有函数都被导出),它需要维护一个可以从外部调用的非内联可寻址函数代码。
要检查您的函数my_function
是否内联,您需要将my_function
函数指针(未内联)与 PC 的当前值进行比较。这是我在我的环境(GCC 7,x86_64)中的做法:
void * __attribute__((noinline)) get_pc () { return _builtin_return_address(0); }
void my_function() {
void* pc = get_pc();
asm volatile("": : :"memory");
printf("Function pointer = %p, current pc = %p\n", &my_function, pc);
}
void main() {
my_function();
}
如果一个函数没有内联,PC的当前值和函数指针的值之间的差异应该很小,否则会更大。在我的系统上,当my_function
未内联时,我得到以下输出:
Function pointer = 0x55fc17902500, pc = 0x55fc1790257b
如果函数是内联的,我得到:
Function pointer = 0x55ddcffc6560, pc = 0x55ddcffc4c6a
对于非内联版本的差异是0x7b
,对于内联版本的差异是0x181f
。
上面的答案非常有用,我只是添加一些我们在编写内联函数时牢记的要点。
请记住,内联只是对编译器的请求,而不是命令。编译器可以忽略内联请求。编译器可能不会在以下情况下执行内联:
1) 如果一个函数包含一个循环。(为,同时,做的时候)
2) 如果函数包含静态变量。
3) 如果一个函数是递归的。
4) 如果函数返回类型不是void,并且函数体中不存在return语句。
5) 如果函数包含 switch 或 goto 语句。
如果函数返回地址,编译器不会使函数内联。