静态链接
编译过程
预处理、编译、汇编、链接
预处理
预处理过程主要处理源码中以“#”开始的预编译指令。
- 将所有的"#define"删除,并且展开所有的宏定义
- 处理所有条件预编译指令,#if #ifdef #elif #else #endif
- 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。注意这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
- 删除所有的注释//和/**/
- 添加行号和文件名标识,比如#2 “hello.c” 2,以便编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
- 保留所有的#pragma编译器指令,因为编译器要使用它们。
gcc -E选项进行预编译。生成.i的预编译文件不包含任何宏定义,所有宏定义均已经展开。所以当我们无法判断宏定义是否正确或头文件是否正确时,可以查看预编译后的文件来排查问题。
编译
词法分析、语法分析、语义分析及优化,生成汇编文件.s
GCC 使用ccl来预编译和编译。
汇编器as、链接器ld
汇编
汇编器是将汇编代码转换成机器可以执行的指令。
链接
将多个目标文件链接成一个可执行文件
编译器
编译器就是将高级语言翻译成机器语言的一个工具。
编译过程:扫描、语法分析、语义分析、源代码分析、代码生成、目标代码优化。
词法分析
源码程序被输入到扫描器,扫描器简单的进行词法分析,使用类似有限状态机的算法将源代码字符序列分割成一系列记号。
词法分析产生的记号分类:关键字、标识符、字面量、特殊符号。
lex程序可以实现词法扫描,它会按照用户之前描述好的词法规则将输入字符串分割成记号。
语法分析
语法分析器将对扫描器产生的记号进行语法分析,生成语法树。整个分析过程采用了上下文无关语法的分析手段。由语法分析器生成的语法树就是以表达式为节点的树。
yacc语法分析器,可以根据用户给定的语法规则对输入的记号序列进行解析,生成一棵语法树。
语义分析
编译器所能分析的语义是静态语义,指在编译期间可以确定的语义。相对的是动态语义,运行期间才能确定的语义。
静态语义包括声明和类型的匹配,类型的转换。
动态语义一般指在运行期出现的语义相关问题,如除0操作,就是运行期语义错误。
经过语义分析后,整个语法树的表达式都被标识了类型。
中间语言生成
源码级优化器可以将编译期间能够确定的值计算出来。
直接在语法树上作优化比较困难,源代码优化器会将整个语法树转换成中间代码,它是语法树的顺序表示,已经非常接近目标代码了。比较常见的有:三地址码、P-代码。
目标代码生成与优化
源代码级优化器产生中间代码标志着下面的过程都属于编译器后端。编译器后端主要包括代码生成器和目标代码优化器。代码生成器将中间代码转换成目标机器代码,十分依赖于目标机器。
最后目标代码优化器对目标代码进行优化,比如选择合适的寻址方式、使用位移代替乘法运算、删除多余的指令等。
链接器
重定位
重新计算各个目标的地址过程。
静态链接
链接过程主要包括了地址和空间分配、符号决议和重定位等步骤。
地址修正的过程叫做重定位,每个要被修正的地方叫一个重定位入口。重定位做的就是给程序中每个这样的绝对地址引用的位置打补丁,使他们指向正确的地址。