编译和链接过程如何工作?
(注意:这是对 Stack Overflow's C++ FAQ 的一个条目。如果您想批评以这种形式提供常见问题解答的想法,那么 the posting on meta that started all this 将是这样做的地方。该问题的答案在C++ chatroom ,FAQ 的想法首先从这里开始,因此您的答案很可能会被提出该想法的人阅读。)
最佳答案
C++程序的编译包括三个步骤:
#include
s, #define
s 和其他预处理器指令。此步骤的输出是没有预处理器指令的“纯”C++ 文件。 预处理
预处理器处理预处理器指令,如
#include
和 #define
.它与 C++ 的语法无关,这就是为什么必须小心使用它。它通过替换
#include
一次处理一个 C++ 源文件带有相应文件内容的指令(通常只是声明),替换宏( #define
),并根据 #if
选择文本的不同部分, #ifdef
和 #ifndef
指令。预处理器处理预处理 token 流。宏替换被定义为用其他标记替换标记(操作符
##
在有意义时允许合并两个标记)。毕竟,预处理器产生一个单一的输出,它是由上述转换产生的 token 流。它还添加了一些特殊标记,告诉编译器每一行来自哪里,以便它可以使用这些标记来生成合理的错误消息。
在这个阶段可以巧妙地使用
#if
产生一些错误。和 #error
指令。汇编
编译步骤在预处理器的每个输出上执行。编译器解析纯 C++ 源代码(现在没有任何预处理器指令)并将其转换为汇编代码。然后调用底层后端(工具链中的汇编程序)将该代码组装成机器代码,以某种格式(ELF、COFF、a.out、...)生成实际的二进制文件。该目标文件包含输入中定义的符号的编译代码(以二进制形式)。目标文件中的符号按名称引用。
目标文件可以引用 undefined symbol 。当您使用声明并且不为其提供定义时就是这种情况。编译器不介意这一点,只要源代码格式正确,就会愉快地生成目标文件。
编译器通常会让您在此时停止编译。这非常有用,因为使用它您可以单独编译每个源代码文件。这提供的优点是,如果您只更改单个文件,则无需重新编译所有内容。
生成的目标文件可以放在称为静态库的特殊文件中,以便以后更容易地重用。
正是在这个阶段,报告了“常规”编译器错误,例如语法错误或失败的重载解析错误。
链接
链接器是从编译器生成的目标文件生成最终编译输出的东西。此输出可以是共享(或动态)库(虽然名称相似,但它们与前面提到的静态库没有太多共同之处)或可执行文件。
它通过用正确的地址替换对 undefined symbol 的引用来链接所有目标文件。这些符号中的每一个都可以在其他目标文件或库中定义。如果它们是在标准库以外的库中定义的,则需要将它们告知链接器。
在这个阶段,最常见的错误是缺少定义或重复定义。前者意味着定义不存在(即它们没有被写入),或者它们所在的目标文件或库没有提供给链接器。后者很明显:在两个不同的目标文件或库中定义了相同的符号。
关于c++ - 编译/链接过程如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6264249/