c++ - 编译/链接过程如何工作?

标签 c++ compiler-construction linker c++-faq

编译和链接过程如何工作?

(注意:这是对 Stack Overflow's C++ FAQ 的一个条目。如果您想批评以这种形式提供常见问题解答的想法,那么 the posting on meta that started all this 将是这样做的地方。该问题的答案在C++ chatroom ,FAQ 的想法首先从这里开始,因此您的答案很可能会被提出该想法的人阅读。)

最佳答案

C++程序的编译包括三个步骤:

  • 预处理:预处理器获取 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/

    相关文章:

    python - 从Python调用C++ 64位共享库

    parsing - 消除 E := EE+|EE-|id 的左递归

    c - C 中的隐式函数声明实际上会生成目标代码吗?

    C++编译和链接

    windows - GHC::在 Windows 上再次链接 sqlite3 失败

    c++ - "VC++"和 "C++"有什么区别?

    c++ - 如果我需要在我的 header 中包含另一个 header 并且我计划在我的 CPP 文件中使用它,我是否也应该将它包含在那里以提高可读性?

    c++ - 从可变宏参数创建成员

    c# - 编写一个极其简单的解析器

    c++ - 无法将C共享库链接到C++程序