一个代码编写完成到生成可执行文件,这其中包括4个步骤:预处理、编译、汇编、链接。现在很多的IDE都让我们忽略了这个过程,更搞不清楚一个编译器的运行过程,我就说说这个过程。
以linux的GCC为例,这四步单独的操作步骤:
预处理:gcc –E hello.c –o hello.i / cpp hello.c > hello.i
编译:gcc –S hello.i –o hello.s / gcc –S hello.c –o hello.s
汇编:gcc –c hello.s –o hello.o / as hello.s –o hello.o / gcc –c hello.c –o hello.o
链接:ld –static …(全部所需的 .o 文件)
预处理器:
在这里就不详细介绍了,这个过程是最简单的,相信大家都明白。
编译器:
编译器的编译的过程:扫描、语法分析、语义分析、源代码优化、代码生成、目标代码优化
扫描器:词法分析。将代码拆成一些列的记号,用lex程序按之前描述好的词法规则将字符分割成一个个记号,实现扫描;
语法分析器:语法分析。将扫描器产生的记号进行语法分析,产生语法树;语法树就是以表达式为节点的树;语法分析的的一个现成的工具——yacc,根据用户给定的语法规则对输入的记号序列进行解析,哦那个人构建出一颗语法树。
语义分析器:语义分析。语法分析只是完成了语法的分析,它并不了解语句的真是意义,如两个指针的乘法,在语法上是正确的;编译器分析的是静态语义,是指编译期可以确定的语义;与之对应的动态语义只有在运行期才能确定的语义;静态语义通常包括声明、类型匹配、类型转换;经过语义分析阶段以后,整个语法树的表达式都被标示了类型,需做隐式转换的会在语法树中插入相应的转换节点。
源码级优化器:中间语言生成。有的表达式的值在编译期就能确定的是可以被优化掉的;中间代码是语法树的顺序表示,它已非常接近目标代码,但它一般跟目标机器和运行时环境是无关的;中间代码类型很多,常见的有三地址码、P-代码;中间代码使得编译器可以分为前端和后端,前端负责产生机器无关的中间代码,后端将中间代码转换成目标机器代码。
代码生成器和目标代码优化器:目标代码生成和优化。源代码级优化器产生中间代码标志着以后的过程都属于编译器后端;代码生成器将中间代码转换成目标机器代码,此过程依赖于目标机器;目标代码优化器对目标代码进行优化,如选择合适的寻址方式、使用位移来代替乘法运算、删除多余的指令等;目标代码中还存在一个问题,其他模块定义的全局变量等的地址怎么确定?事实上,定义其他模块的全局变量和函数在最终运行时的绝对地址都要在最终链接的时候才能确定。
汇编器:
汇编的过程在编译器中基本就完成了,就不多讲了。
链接器:
链接器的链接过程,先说静态链接(模块拼装)
链接过程主要包括地址和空间分配、符号决议(这个名字还有很多别的名称,“决议”更倾向于静态链接,而“绑定“更倾向于动态链接)、重定位等步骤。
关于动态链接的相关知识,现不在本文章中阐述了。。。