编译过程
编译过程可分为6步,即词法分析、语法分析、语义分析、源代码优化、目标代码生成、目标代码优化。如下图所示:

若使用中间代码形式则有7步,即词法分析、语法分析、语义分析、中间代码生成、中间代码优化(独立于机器)、目标代码生成、依赖于机器的目标代码优化。中间代码优点在于不依赖于具体的机器,且便于对源码进行优化。
一、词法分析:
又称扫描,采用有限自动机(finite automata)算法。
NFA:不确定的有限自动机。满足以下条件:
1. 有限状态集合S;
2. 有限输入符号的字母表Σ;
3. 状态转移函数move;
4. 开始状态 sSUB{0};
5. 结束状态集合F,F ∈ S。
状态集合有限,开始状态唯一,终止状态可以不唯一 ,允许ε(长度为0的记号称为空记号,记为ε,读作epsilon)变换但输入符号集合不允许代表空串的ε。
DFA:确定的有限自动机。与NFA的区别:
1.任何状态都不允许ε变化
2对任何状态s和符号a,最多一条标记为a的边离开s,即某个状态对于每一种输入最多只有一种状态转移。
由于计算机不容易模拟NFA,通常采用子集构造法,将NFA转换为DFA。
Lex可从基于正规式的描述来构造词法分析器。
二、语法分析
语法分析器读取词法分析器提供的记号流,生成以表达式(Expression)为节点的语法树。编译器常用的语法分析方法有自上而下和自下而上两种,这两种分析法都只能处理上下文无关文法(2型文法)的子类。语法分析器可由Yacc构造。
三、语义分析
语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查,审查源程序有无语义错误。编译器分析的为静态语义(static semantic),即在编译期可以确定的语义。与之相对的动态语义(dynamic semantic)就是运行时才能确定的语义。
静态语义:声明和类型的匹配,类型转换等。动态语义:0做为除数。在经过语义分析后语法树的节点被标明类型。
四、源代码优化
直接对源代码在语法树上进行优化往往比较困难,源代码优化器(Source Code Optimizer),将整个语法书转化为中间代码(Intermediate Code),再优化中间代码
中间代码是是语法树的顺序表示,它与目标机器和运行环境无关的,不包含数据的尺寸、寄存器的名字等依赖于机器的信息。中间代码使得编译器被分为前端和后端。编译器前端负责产生机器无关的中间代码;编译器后端将中间代码转化为目标机器代码。
常见的中间代码有三地址码等
五、六 目标代码生成与优化
生成汇编语言,对目标代码进行优化,比如选择合适的寻址方式等。
C语言程序编译过程
gcc生成可执行程序的4个步骤:预处理(Processing)、编译(compilation)、汇编(Assembly)和链接(Linking)。如下图所示:

预处理主要处理包括以下过程:将所有的#define删除,并且展开所有的宏定义;处理所有的条件预编译指令,比如#if #ifdef #elif #else #endif等;处理#include 预编译指令,将被包含的文件插入到该预编译指令的位置;删除注释;添加行号和文件标识等。
链接通过调用链接器ld来链接程序运行需要的所有目标文件,以及所依赖的其它库文件,最后生成可执行文件。链接的主要过程包括:地址和空间分配(Address and Storage Allocation),符号决议(Symbol Resolution),重定位(Relocation)等。链接分为静态链接和动态链接。静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行文件会比较大。动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去。

浙公网安备 33010602011771号