编译原理 笔记

2017/02/25

1. 预处理

处理以#开头的的预编译指令 #include#define#if#else

2. 编译

词法分析

代码通过(Scanner扫描器) 被识别为 关键词 标识符 字面量( int,string)特殊符号(+,=),他们可以被叫为记号 Token,他们将会在生成语法树被使用到。

另外词法分析程序Lex 可以按需要改变自己的词法规则生成属于自己的词法分析器。

语法分析

生成以 表达式 为节点的 语法树,最小的表达式可以是 字面亮 也就是上面的Token ,按照特定的语法规则,将它们组织起来。

像词法分析程序Lex 一样,语法分析程序yacc也可以按需要改变自己的语法分析规则生成属于自己的语法分析器。

语义分析

进入语义分析后,编译器可以理解静态语义,也就是编译器能确定的语义,可以分析出一些错误,比如将一个浮点型数值付给指针,0作为除数等。经过语义分析后,整个语法输了的表达式都被标示了类型

中间语言生成

由于生成的语法树,对它优化并不方便。需要转化到其他类型的语言,比如三地址码格式 :

t1 = 2+6 ; 
t2 = index + 4 ; 
t3 = t2 * t1 ; 
array[index] = t3 ;

3. 汇编

代码生成器 (Code Generator) 将中间代码转换成目标机器代码 eg:汇编,这个阶段十分依赖目标机器,因为不同的机器有着不同的字长,寄存器,浮点数数据类型。

目标代码优化器 (Target Code Optimizer) 对生产的机器码进行优化,选择合适的寻址方式,使用位移来代替乘法运算等。

要注意,其中array和index的地址可能是还有没确定的。那么怎么能将它编译成最终的指令呢?事实上,定义其他模块的的全局变量和函数在最终运行是的绝对地址都要在最终链接的时候才能确定,所以现代的编译器可以将一个源代码编译成一个 未连接 的目标文件,然后由连接器最终将这些目标文件链接在一起,形成可执行文件。

4. 连接

连接器先将指令的地址 符号化 ,这个地址可能是一段子程序 函数 的起始地址,也可以是一个变量的起始地址。

之后就是将要连接在一起的模块的这些符号化的地址加以引用和修正,主要过程有 地址和空间的分配符号决议重定位

举个例子:比如main.c中使用了 foo 函数,连接器在连接时候会根据你所引用的符号 foo 自动的去相应的func.c模块查找 foo 的地址,将main.c中的 foo 的函数地址修正到真正的 foo函数地址。

参考:[程序员的自我修养]

Post Directory