源程序的编译过程

内容来自<<程序员的自我修养-链接,加载和库>> 非常好的书.


编译过程一般分为6个步骤:

源程序的编译过程_第1张图片

扫描:

源代码程序被输入到扫描器(Scanner),扫描器的任务很简单,他只是进行词法分析,将源码中的字符序列分割成一系列记号;

词法分析产生的记号一般可以分为如下几类: 关键字,标识符,字面量(数字字符串等),和特殊符号(如 加号 ,减号等).在扫描器识别记号的同时,也完成了其他的工作.比如将标识符存放在符号表,将数字,字符串常量存放到文字表等,以备后面的步骤使用.(对于一些有预处理的语言,如c语言,它的宏替换和头文件包含等工作一般不归如编译器的范围,而交给一个独立的预处理器)


源程序的编译过程_第2张图片

语法分析:

接下来语法分析器(Grammar Parser) 将由扫描器产生的记号进行语法分析,从而产生语法树(Syntax tree).整个过程采用了上下文无关(context-free Grammar) 的分析手段.由语法分析器产生的语法树就是以表达式为节点的树.我们知道C语言的一个语句是一个表达式,而复杂的语句是很多表达式的组合.在语法分析的同时,很多富豪的优先级和含义也被确定下来了.(比如* 好是表示乘法表达式,还是表示对指针内容取值等).如果出现表达式不合法,比如括号不匹配等,编译器就会报告语法分析阶段的错误

语法分析也由一个现成的独立工具叫做yacc.对于不同的编程语言,编译器的开发者只需改变语法规则,而无需为编译器编写一个语法分析器.

源程序的编译过程_第3张图片

语义分析:

接下来是语义分析.有语义分析器(Semantic Analyzer)来完成.与法分析仅仅是完成了对表达式的语法层面的分析,但是它并不了解这个语句是否正真有意义.比如C语言里面两个指针做乘法运算是没意义的,但是这个语句在语法上是合法的.

编译器所能分析的语义是静态语义(static semantic),所谓静态语义是指在编译期可以确定的语义,预支对应的动态语义就是指只有在运行期才能确定的语义.

静态语义通常包括声明和类型匹配,类型装换.比如一个浮点型的表达式复制给一个整形表达式时,其中隐含了一个浮点型到整形的转换过程,语义分析过程需要完成这个步骤.比如将一个浮点型复制给一个指针的时候,语义分析程序会发现这个类型不匹配,编译器就会报错.动态语义一般是指在运行期间出现的语义相关的问题,比如将0作为除数是一个运行期语义错误.

经过语义分析后,整个语法书的表达式都背标识饿类型,如果这些类型需要做隐式转换,语义分析程序会在语法书中插入相应的转换节点.


源程序的编译过程_第4张图片


源代码优化:

现代编译器有着很多层次的优化,往往子啊源代码级别会有一个优化过程.这里描述的源代码级优化器在不同的编译器中可能会有不同的定义域或者有其他的一些差异.列如: 在上面的(2+6)这个表达式可以被优化掉(被优化成8).因为它的值在编译期就可以被确定.类似的还有很多其他复杂的优化过程.直接在语法树上做优化比较困难,语义源代码优化器往往将整个语法书转换成中间代码,它是语法书的顺序表示,其实它已经非常接近目标代码了.大师它一般跟目标机器运行和运行是环境是无关的,比如他不包含数据的尺寸,变量的地址和寄存器的名字.

中间代码使得编译器可以被分为前端和后端.编译器前端负责产生机器无关的中间代码,编译器后端将中间代码转换成目标机器代码.这样对于一些可跨平台的编译器而言,它们可以正对不同的平台使用同一个前端,和针对不同的机器平台的数个后端.


目标代码生成:

源代码优化器产生中间代码标志着下面的过程都属于编译器后端.编译器后端主要包括代码生成器(Code Generator) 和目标代码优化器(Target Code Optimizer).

代码生成器将中间代码转换成目标机器代码,这个过程十分依赖于目标机器,因为不同的机器有着不同的字长,寄存器,整数数据类型和浮点数数据类型.

目标代码优化:  

最后目标代码优化器对目标代码进行优化,比如选择合适的寻址方式没使用位移来替代乘法运算,删除多余的指令等.



连接:

连接的主要内容就是把各个模块之间的互相引用的部分处理好,使得各个模块之间能够正确的衔接.从原理上来讲就是把一些指定对其他符号地址的引用加以修正.连接的过程主要包括了地址分配(Address and storage  Allocate ) ,符号决议(Symbol Resolution) 和重定位(Relocation) 等步骤. 




你可能感兴趣的:(C)