.c/.cpp文件生成到执行过程
预编译:
gcc -E main.c -o main.i
main.i
- 处理源代码文件中的“#”开始的预编译指令。如“#include”、“#define”等。具体如下:
- 宏替换:将所有的“#define”删除,并展开所有的宏定义。
- 处理条件预编译指令,如“#if" "#endif" "#elif" "#else"指令:
表达式1为真,编译语段1,否则判断表达式2;表达式2为真编译语段2,否则编译语段3
如果之前定义了这样的宏名,则编译语段。
如果没有定义这样的宏名,则编译语句段
- 处理“#include”预编译指令,将被包含的头文件插入到预编译指令的位置。(递归插入,被包含的文件还可能包含其他文件)
- 删除所有注释(“//”、“/* */”)。
- 添加行号和文件标识(以便编译时期,出错显示行号、调试使用行号信息)。
- 保留所有#pragma编译器指令。
编译:
gcc -S main.i -o main.s
main.s
- 词法分析:源代码经过扫描器,将源代码的字符序列分割成一系列的记号(关键字、标识符、字面量(数字、字符串)、特殊符号(如“+”“-”)),同时将标识符存放到符号表,数字、字符串常量放到文字表等工作,以备后续使用。
- 语法分析:语法分析器对扫描器产生的记号进行语法分析,生成语法树(以表达式为节点的树,检验表达式的逻辑性)。
- 语义分析:对静态语义(编译期可以确定的语义,包括声明、类型匹配、类型转换等)进行分析。如浮点型表达式赋给整型表达式时,类型不匹配,语义分析隐含执行了浮点型到整型转换的过程。
- 代码优化:源代码级优化器(优化语法树,如替换重复或简单的表达式)进行优化。
汇编:
gcc -C main.s -o main.o
a.out
- 汇编器将汇编代码变成机器可以执行的指令,计算机就可以根据汇编指令和机器指令的对照表进行翻译生成可重定位的二进制目标文件。
链接:
./a.out(可执行文件)
- 空间与地址分配、合并段和符号表:扫描所有的输入目标文件,获得各段的长度、属性和位置,并且将输入目标文件中的符号定义和符号引用收集放到一个全局符号表。同时,链接器计算出输出文件中各个段合并后的长度与位置,并建立映射关系。
- 符号解析与重定位:使用上步信息,进行符号解析与重定位、调整代码中的地址(虚拟地址映射物理地址)等。
- (强弱符号:已初始化的全局变量为强符号,未初始化的全局变量为弱符号。规则:两强,重定义错误;一强一弱,强为所有地址;两弱,编译器决定(一般字节数大)。在汇编完成前,不清楚是否存在强符号,则将变量放入COM块中。)