一、准备
步骤 | 内容 | 例子 |
---|---|---|
配置 | 确定当前的系统环境,比如软件的安装位置在哪里、需要安装哪些组件等等,configure脚本文件用来保存配置信息 | |
确定标准库和头文件的位置 | 从配置文件中知道标准库和头文件的位置;配置文件会给出一个清单,列出几个具体的目录。等到编译时,编译器就按顺序到这几个目录中,寻找目标 | |
确定依赖关系 | 源码文件之间存在依赖关系,编译器需要确定编译的先后顺序 | 例如:A文件依赖于B文件 (1)只有在B文件编译完成后,才开始编译A文件;(2)当B文件发生变化时,A文件会被重新编译。 |
二、预编译
编译头文件及源代码文件中的以“#”开头的预编译指令,先编译头文件,再编译源码(保证头文件只需编译一次,不必每次用到的时候,都重新编译);
规则:
- 删除所有的#define,展开所有的宏定义;
- 处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”;
- 处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件;
- 删除所有的注释,“//”和“/**/”;
- 保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件被重复引用;
- 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号;
三、编译
确认所有指令符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码
1、过程
步骤 | 内容 |
---|---|
词法分析 | 从左到右一个一个读入源程序,识别一个单词或者符号,并进行归类; |
语法分析 | 再词法分析的基础上,将单词序列分解成各类语法短语,如“表达式”“语句”等; |
语义分析 | 审查源程序是否有语义的错误,查看类型是否匹配,当不符合规范时,会报错; |
中间代码生成 | 编译程序将源程序变成了一种内部表示形式,这种内部表示形式叫做中间代码或中间语言; |
目标代码生成 | 将优化后的中间代码变成指令代码或者汇编代码; |
2、优化
优化定义:所谓代码优化是指对程序代码进行等价变换(指不改变程序的运行结果)。程序代码可以是中间代码,也可以是目标代码。
等价的含义是使得变换后的代码运行结果与变换前代码运行结果相同。
优化的含义是最终生成的目标代码短(运行时间更短、占用空间更小),时空效率优化。
原则上,优化可以在编译的各个阶段进行,但最主要的一类是对中间代码进行优化,这类优化不依赖于具体的计算机。
优化目的:在不改变程序运行效果的前提下,对被编译的程序进行等价变换,使之能生成更加高效的目标代码。
优化原则:
- 等价原则。经过优化后不应该改变程序运行的结果。
- 等效原则。使优化后所产生的目标代码运行时间较短,占用的储存空间较小。
- 合算原则。应尽可能以较低的代价取得较好的优化效果。
优化方式:以程序的基本块(基本块指只有一个控制流入口、一个控制流出口的程序块)为基础,基本块内的优化叫局部优化,跨基本块的优化为全局优化,循环优化是针对循环进行的优化,是全局优化的一部分。
- 删除多余运算
- 代码外提
- 强度削弱
- 变换循环控制条件
- 合并已知量和复写传播
- 删除无用赋值
四、 汇编
把汇编语言代码翻译成目标机器指令
目标文件中所存放的是与源程序等效的机器语言代码。目标文件由段组成,通常一个目标文件中至少有两个段:代码段和数据段;
代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中要用到的各种全局变量或静态的数据。
五、链接
将有关的目标文件彼此相连接,使得所有的目标文件能够成为一个统一的整体
- 静态链接
在这种链接方式下,函数的代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码; - 动态链接
在此种方式下,函数的代码被放到动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。