C++ g+的内部工作原理+;编译器,用于假人

C++ g+的内部工作原理+;编译器,用于假人,c++,compiler-construction,g++,C++,Compiler Construction,G++,在用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文件编

在用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
s等)

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

下一个逻辑步骤称为语法分析,根据语言的语法(语法)将标记流转换为抽象结构。这是报告语法错误的地方。例如,
inta=3可能被解析为声明(sym(a)、表达式(concent(3))

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

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

在实践中,这些过程中的一些是组合的(例如,标记化通常在语法分析阶段按需进行,这也可能是构造语义上有意义的符号表等)。还有各种各样的优化(有些是集成的,有些是在单独的过程中)贯穿始终。例如,我相信GCC会将程序转换为一个中间表示,以进行数据流分析,从而更好地优化代码

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


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

我们不知道这些文件是如何处理页眉的。头文件没有链接到那么多…也就是说,当预处理器读取文件时,当它遇到一个#include时,它会将.h文件逐字插入到它的内存副本中。换句话说,对于编译器来说,.c文件中的#include“bar.h”行与您使用文本编辑器在同一行插入bar.h的内容完全相同。此外,.o文件已编译(.o代表对象文件)。下一步是将它们链接到一个可执行文件中。我想知道为什么我在这个问题上被否决,这可能是从一个相对不切实际的角度来看的,但我认为它提出了一些很好的问题,卡梅隆给出了一个很好的解释,这对我很有用,也可能对其他具有类似经验的人很有用。有什么问题吗?