首先,整个过程大致如下图:
预编译阶段做的事:
1、删除#define并做文本替换。
2、处理所有的条件预编译指令,比如“# if","# ifdef",”# elif",“# else”,“# endif”。
3、处理“# include"预编译指令,将被包含的文件插入到该预编译指令的位置,递归展开头文件。
4、删除所有的注释,如:"//","/**/"。
5、添加行号和列号,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号 。
6、保留所用的#program编译器指令,因为编译器需要使用它们。
编译阶段做的事:
1、词法分析。
2、语法分析。
3、语义分析。
4、代码优化。
5、生成指令。
汇编阶段做的事:
1、指令翻译成二进制。
链接过程:
链接过程是由链接器进行操作的。 链接器(英语:Linker),又译为链接器、连结器,是一个程序,将一个或多个由编译器或汇编器生成的目标文件外加库链接为一个可执行文件。在编译过程中都已经得到了可执行文件。大多数操作系统都提供静态链接和动态链接这两种链接方式,以下是连接过程:
静态链接(编译时):
链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
优点: 只需保证在开发者的计算机有正确的库文件,在以二进制发布时不需考虑在用户的计算机上库文件是否存在及版本问题.
缺点: 生成的可执行文件体积较大。当初正是为了避免此问题,才开发了动态库技术。
动态链接(加载、运行时):
所谓动态链接,就是把一些经常会共用的代码(静态链接的OBJ程序库)制作成DLL档,当可执行文件调用到DLL档内的函数时,操作系统才会把DLL档加载存储器内,DLL档本身的结构就是可执行档,当程序有需求时函数才进行链接。透过动态链接方式,存储器浪费的情形将可大幅降低。静态链接库则是直接链接到可执行文件。
DLL档本身也是可执行文件, 在程序执行的时候直接进行动态调用即可.
链接阶段做的事:
1、合并段和符号表。
2、符号解析。
3、分配地址和空间。
4、符号重定位。假设我们有个全局变量叫做var,它在目标文件A里面。我们在目标文件B里面要访问这个全局变量,比如我们在目标文件B里里面有这么一条指令:mov1 s0x2a,var。这条指令就是给这个var变量赋值0x2a,相当于C语言里面的语句var=42。然后我们编译目标文件B,得到这条指令机器码,如下图所示:
由于我们在编译目标文件B时,编译器你并不知道变量var的目标地址,所以编译器在没法确定地址的情况下,将这条mov指令的目标地址设置为0,等待链接器在将目标文件A和B链接起来的时候再将其修正。我们假设A和B链接后,变量var的地址确定下来为0x1000,那么链接器将会把这个指令的目标地址部分修改为0x10000。这个地址修正的过程也被叫做重定位,每个要被修正的地方叫一个重定位入口。