生成可执行文件的四个过程
当编写和构建计算机程序时,预处理、编译、汇编和链接是将源代码转化为可执行程序的关键过程。以下是对每个阶段的详细解释:
1. 预处理(Preprocessing):将.c/.cpp文件中的头文件展开、宏展开,生成.i文件
预处理是编译过程的第一个阶段,它在编译之前对源代码进行处理。预处理器执行以下任务:
- 宏替换:根据预定义的宏规则,将源代码中的宏调用替换为对应的文本。
- 头文件包含:将头文件的内容插入到源代码中,以便在编译时可以使用头文件中定义的内容。
- 条件编译:根据条件指令选择性地编译特定部分的代码。
2. 编译(Compilation):将预处理之后的.i文件,生成 .s 汇编文件
编译是将预处理后的源代码转换为中间代码(通常是汇编语言或字节码)的过程。编译器在这个阶段执行以下任务:
- 词法分析:将源代码分解为标记(tokens),如关键字、标识符、运算符等。
- 语法分析:根据语言的语法规则,将标记组合成语法树(syntax tree),用于表示程序的结构。
- 语义分析:检查语法树是否符合语言的语义规则,如变量的声明和使用是否正确。
- 代码生成:将语法树转换为目标代码(通常是汇编语言或字节码),包括指令序列和数据结等。
而且,此时在编译阶段,符号表开始形成并逐步积累符号的信息。符号表是一个数据结构,用于存储程序中使用的符号及其相关信息。
3. 汇编(Assembly):将.s 汇编文件,生成.o 目标文件
汇编是将编译生成的汇编语言代码转换为可执行机器指令的过程。在这个阶段,汇编器执行以下操作:
- 读取编译生成的汇编语言代码。
- 将汇编代码转化为机器码,即二进制表示的指令。
- 为每条指令生成相应的机器码,并解析变量和函数的引用。
汇编器继续使用符号表,根据汇编代码中的符号引用解析变量和函数的定义,并为它们分配相应的内存地址。
4. 链接(Linking):将.o文件链接,生成目标文件.exe或.out
链接是将编译和汇编生成的目标代码文件以及其他必要的库文件组合在一起,生成最终的可执行程序或可加载模块的过程。链接器执行以下任务:
- 符号解析:解析目标代码中的符号引用,如函数调用和变量引用。
- 地址分配:为每个符号分配内存地址,确定符号在内存中的位置。
- 重定位:修改目标代码中的相对地址,使其正确地指向符号的实际位置。
链接器使用符号表中的信息来解析符号引用并更新目标代码中的地址。
符号表
我们发现,上面反复提到了符号表。那么符号表是什么呢?符号表是一个数据结构,用于存储程序中使用的符号(如变量名、函数名、类名等)以及与之相关的信息。它在编译和链接过程中起着重要的作用,用于符号解析、类型检查、地址分配和重定位等操作。
在符号表中,每个符号都有一个对应的条目,包含了以下信息:(只是简要概括,并未全部列出)
1. 符号名称(Symbol Name):表示符号的名称,如变量名、函数名、类名等。
2. 符号类型(Symbol Type):指示符号的类型,如变量、函数、类等。
3. 数据类型(Data Type):描述符号所代表的数据类型,如整数、浮点数、字符串等。
4. 存储信息(Storage Information):指示符号在内存中的存储方式,如静态存储、栈上存储或堆上存储。
5. 内存地址(Memory Address):如果符号是变量或函数,符号表会记录其在内存中的地址或相对地址。
6. 作用域(Scope):指示符号的可见范围,即在哪个部分或代码块中可访问该符号。
7. 变量属性(Variable Attributes):包括符号的大小、访问权限、生命周期等属性。
8. 参数列表(Parameter List):如果符号是函数,符号表会记录函数的参数列表,包括参数的名称和类型。
9. 返回类型(Return Type):如果符号是函数,符号表会记录函数的返回类型,即函数执行后的返回值类型。