二、编译和链接

1. 编译的四个步骤

预处理(Propressing)
编译(Compilation)
汇编(Assembly)
链接(Linking)

1.1 预处理(Propressing)

预处理过程主要是处理源代码中以“#”开始的预编译指令,生成.i文件,主要处理规则如下:

  • 删除所有“#define”,展开所有的宏定义。
  • 处理所有条件预编译指令,比如“#ifdef”。
  • 处理“#include”预编译指令,将被包含的文件插入到指令所在的位置。
  • 删除注释。
  • 添加行号和文件名标识,用于产生调试信息。

1.2 编译(Compilation)

编译过程是把预处理之后的文件经过词法分析、语法分析、语义分析、中间代码生成、目标代码生成与优化这些步骤,生成相应的汇编代码.s文件。

1.2.1 词法分析

预处理之后的源代码被输入到扫描器(Scanner),运用算法将代码的字符序列分割成一系列的Token,并对Token分类:关键字、标识符、字面量(数字、字符串等)和特殊符号(运算符等)。

1.2.2 语法分析

对词法分析得到的Token进行语法分析,生成语法树(如下)。语法树是以表达式(Expression)为结点的树。符号和数字是最小的表达式,作为叶节点。语法分析的同时,运算符的优先级和含义也被确定。如果表达式不合法,编译器会报语法错误。

array[index] = (index + 4) * (2 + 6) 的语法树

1.2.3 语义分析

语法分析只完成了表达式语法层面的分析,并不了解表达式是否真正有意义,比如指针乘法在语法上是合法的。编译器所能分析的是静态语义,包括类型匹配和转换。
语义分析之后,语法树的表达式会被标识类型,如果有类型隐式转换,会插入相应的转换节点。

1.2.4 中间代码生成

现代编译器往往在源代码级别会有一个优化,比如上面的2+6,会被优化成8。直接在语法树上做优化比较困难,因此往往会把语法树转换成中间代码(Intermediate Code),它是语法树的顺序表示。
中间代码很接近目标代码,但是跟平台无关,比如不包含数据的尺寸、变量地址和寄存器名字等等。中间代码的表示形式常见的有三地址码(Three_address Code),里面对三个变量地址操作。

1.3 汇编(Assembly)

将汇编代码转换成机器指令,由于每一条汇编语句几乎都对应一条机器指令,只需根据对照表一一翻译就可以了。
主要包括代码生成器(Code Generator)和目标代码优化器(Target Code Optimizer)。代码生成器将中间代码转换成目标机器代码,优化器对目标代码进行优化,比如选择合适的寻址方式、删除多余的指令等等。

1.4 链接(Linking)

目标代码中有变量定义在其他模块怎么办?这是编译无法解决的,需要在链接的时候才能确定。所以现代编译器是将源代码文件变成从未连接的目标文件,有链接器最终将目标文件链接成可执行文件。
从原理上来讲,链接就是修正外部变量的地址。(静态链接)

你可能感兴趣的:(二、编译和链接)