第二章 编译与链接
预处理
预处理主要处理源代码文件中以#
开头的预处理指令,预处理的工作如下:
- 删除所有
#define
,展开所有宏定义; - 处理所有条件预处理指令(如
#if
、#endif
、#elif
、#else
、#endif
); - 处理
#include
预处理指令,将被包含的文件插入预处理指令的位置。 - 删除所有的注释;
- 添加行号与文件名标识;
- 保留所有
#pragma
编译器指令;
gcc使用如下命令进行预处理。
1 | gcc -E hello.c -o hello.i |
编译
- 词法分析:使用一种类似于有限自动机的算法将源代码的字符序列分割为一系列的记号。在Ubuntu上有一个flex的程序可实现词法分析。
- 语法分析:使用上下文无关语法对词法扫描的记号进行语法分析,生成语法树。在Ubuntu是有一个bison的程序可实现语法分析。
- 语义分析:静态语义分析包括声明、类型的匹配与转换。
- 中间优化:源代码优化器将语法树转换成中间代码,然后对中间代码进行优化。中间代码使得编译器被分成前端与后端,前端负责产生与机器无关的中间代码,后端将中间代码转换成目标机器代码。这样,跨平台的编译器只需要一个前端,而针对不同的目标机器设计不同的后端。
- 目标代码生成与优化:后端包括代码生成器与目标代码优化器。
经过上述步骤得到的目标代码还有一个问题没有解决——定义在其他模块的变量与函数的地址怎么确定?这需要借助链接器!
汇编
将汇编代码转换为机器语言。
链接
链接包括地址与空间分配、符号绑定与重定位(链接器在链接时修正跨模块函数、变量的地址)。