C++ 编译链接流程

预处理#

这一阶段主要处理以 # 开头的代码段,不进行语法分析,更多的是文本替换。

  • 宏展开:将所有 #define 的宏定义在调用处展开。
  • 头文件包含:处理 #include 指令,将所有引用的文件,比如 .h、.hpp,完整复制到当前文件。
  • 条件编译:根据 #ifdef、#ifndef、#endif 决定保留或删除哪些代码。
  • 删除注释:删除单行注释 // 与多行注释 /* */。

编译#

这一阶段会将预处理后的文本编译为与 CPU 架构紧密相关的汇编语言,同时也是最复杂的一步。

  • 语法分析:检查代码是否存在语法错误,比如是不是少了分号、是不是使用了没有定义的变量等。
  • 语义分析:检查类型是否匹配、传入函数的参数是否正确等。
  • 代码优化:编译器会尝试对已有代码进行性能上的优化,比如循环展开、常量折叠等。
  • 生成汇编:确保代码没有错误之后,编译器会将 C++ 这类高级语言翻译为汇编语言指令。

汇编#

这一阶段要做的是根据汇编语言指令,一对一地将其翻译为机器能直接识别的二进制码。最终输出的文件是 Object File,在 Windows 中是 .obj 文件,在 Linux 中是 .o 文件。

注意:此时各个文件仍然是孤立的,所以诸如全局变量、外部函数调用等,在 Object File 中只会使用占位符来表示。不过如果函数的定义与声明都写在 .h 文件中,就不需要占位了。

链接#

这一阶段会将一个或多个 .obj 文件,以及系统或第三方的库文件,比如 .lib 等,链接在一起,最终形成 .exe 文件。

  • 符号解析:找到当前文件所使用的外部函数,将占位符指向函数在外部 .cpp 文件中的具体实现地址。
  • 重定位:合并所有目标文件的代码段与数据段,并为其分配最终地址。
  • 库引入:对于静态库,直接将其打包进可执行文件;对于动态链接库,比如 .dll,则生成运行时所需的调用信息。