g++ 编译器的内部工作原理,用于傻瓜

The inner workings of the g++ compiler, for dummies

本文关键字:用于 傻瓜 工作 编译器 内部 g++      更新时间:2023-10-16

我对使用 g++ 编译 C/C++ 代码时的编译阶段有一个非常基本的了解,但我想要确认、澄清和额外的智慧珍珠,请。

对于这组文件:

main.c
foo.h
foo.c
bar.h
bar.c

这些调用执行以下操作...

g++ -c foo.c
g++ -c bar.c
g++ -c main.c

头文件现在已添加到源文件中,所有这些 .c 文件都编译为 .o 文件。

g++ -o main.out main.o foo.o bar.o

现在,所有.o文件都链接到一个可执行文件中 - main.out .

.c文件被编译成目标文件,然后链接到最终的二进制文件中。目标文件基本上是二进制文件的未最终确定部分(它们包含在.c文件中定义的函数等的编译机器代码(。

在编译期间,.c文件包括头文件,这些文件实际上只是在#include指令所在的位置扩展。从这个意义上说,.c文件是独立的,不需要单独编译标头;它们都是单个翻译单元的一部分,该翻译单元转换为单个对象文件。

编译的第一步是运行预处理器;这是一个花哨的文本操纵器,可以处理以#开头的所有行(因此,它执行#include指令和条件#ifdef的扩展等(。

然后,翻译单元的文本被标记(这称为词法分析(:字节被转换为最简单的可识别标记,例如"."变成一个点,"++"变成一个"增量",关键字被识别,变量名称被解析为整个实体(标识符(。令牌仍然没有意义,但它们比字节流更容易使用。

下一个逻辑步骤称为句法分析,根据语言的语法(语法(将标记流转换为抽象结构。这是报告语法错误的地方。例如,int a = 3;可以被解析为 declaration(sym(a(, expression(constint(3(((。

之后的下一个逻辑步骤是语义分析,它赋予句法结构以意义 - 例如,解析器可能会生成二十个具有相同名称的变量声明,但这在语义上毫无意义。此处报告了更多错误,例如,不会从所有控制路径返回的非 void 函数。

最后,还有一个代码生成步骤,它选择低级CPU指令来执行翻译单元的语义结构。这实际上是一个巨大的"步骤",可能包括在生成最终指令代码之前将语义数据结构(通常以抽象语法树或AST的形式(转换为较低级(中间(表示的进一步转换。

在实践中,其中一些传递是组合的(例如,标记化通常在句法分析阶段按需发生,也可能构建语义上有意义的符号表等(。还有各种优化(有些集成,一些在单独的传递中(贯穿始终。例如,我相信GCC将程序转换为SSA中间表示,以进行数据流分析,以实现更好的代码优化。

然后,生成的指令(全局变量和静态变量等(将转储到目标文件中。然后将目标文件链接到可执行文件中(全局变量的地址、其他中定义的函数、外部对象文件和动态/共享库此时解析并在最终代码中修复(。

这些都不是 gcc 特有的;这适用于大多数(所有?(C++(和 C(编译器。