我已经研究过,在编译时链接发生并且在运行时执行。在每个函数的内联函数中,调用代码由内联函数定义替换,但函数调用在运行时完成。但是当我审阅一些书籍时,在编译时替换了代码。谁能告诉我在编译时或运行时完成内联代码替换?
答案 0 :(得分:5)
在编译时链接发生并在运行时执行
不完全。
在 build 时间,会发生三个阶段:
预处理器通过文本替换准备每个翻译单元(宏调用被宏定义的文字文本替换,#include
被替换为包含文件的内容等等。)。
没有进行非文本处理(它不了解程序结构或含义)。
编译器解析翻译单元并发出目标代码。
这包括代码生成之前,期间和之后的优化。函数内联传统上是这些优化之一,因为它是在生成调用站点代码时发生的。
链接器将编译的目标文件链接到可执行文件中。
原则上,链接根本不会改变目标代码,除了符号表和偏移之外。
一些编译器做现在提供链接时优化,这使得这张图片略显模糊。除此之外,具有非内联定义的函数可能会在构建周期的后期内联。
在任何一种情况下,函数内联都是在构建时执行的;此决定和生成的代码都内置在可执行文件中。
在运行时,当实际执行调用时,会发生以下两种情况之一:
编译器没有内联调用,这意味着有一个实际的调用指令。控制转移到该功能,它被执行,当它返回控制时,回传给通话后的指令
编译器内联调用,并且函数代码的主体存在,执行只是继续进入(并通过)它。
在第二种情况下,你不会考虑在运行时发生的内联 - 它仍然发生在代码生成的编译时。
请注意,此编译模型并不普遍。例如,在Java中,"目标代码"是虚拟机的字节码,而不是本机机器码。这通常是在运行时编译的,这意味着内联和其他优化也可以在运行时进行。
答案 1 :(得分:0)
您可以将内联代码视为在编译时进行的文本替换。它有点复杂,因为这正是类似函数的宏所做的,但它们在概念上是相似的。
例如,请执行以下操作:
#include <iostream>
using namespace std;
inline int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
cout << max(5,9) << endl; // "9"
return 0;
}
不是像没有内联那样调用max,而是在编译成代码时将其替换为代码
int main()
{
cout << (5 > 9 ? 5 : 9) << endl;
return 0;
}
所以它仍在解决(大于9?等等),最终会打印9
,但不必调用该函数。
编辑: 它与宏不同的原因是安全性和期望相关。想象一下我们有
#define MAX(a,b) ((a)>(b) ? (a) : (b))
作为一个宏。如果要使用
int a=5;
int b=9;
cout << MAX(a++, b++) << endl; // "10"
cout << a << ' ' << b << endl; // "6 11"
我们希望a
和b
分别打印6和10。但是,在宏中,这被替换为
((a++)>(b++) ? (a++) : (b++))
调用此功能后,我们会将(a++)>(b++)
部分的后增量设为a=6
和b=10
。然后我们跟进if语句,然后运行(b++)
(其他条件),然后提供b=11
,比预期多一个。
使用inline,我们获得了预期的结果,因为它将在语句运行后执行这些后增量。
int a=5;
int b=9;
cout << max(a++, b++) << endl; // Inline: "9"
cout << a << ' ' << b << endl; // "6 10"