我们在程序编译时会经历 预编译---编译---汇编---链接 这四个阶段。
在高级语言中这些步骤不易察觉,下面是几个阶段要处理的内容:
预编译 :此过程主要处理源代码中以#开头的预编译指令,如 #if ,#include,#define等
1.将所有的#define删除,并且展开所有的宏定义。
2.处理所有条件预编译指令,比如:#if ,#endif,#ifdef等
3.处理#include预编译指令,将被包含的文件插入到此指令的位置。注意:这个过程是递归调用的,也就是说这个文件 也可能包含其它文件。
4.删除所有的注释,如://,/**/等
5.添加行号和文件名标识名,以便编译器产生调试用的行号信息及用于编译时产生的编译错误和警告时能显示它的行号。
6.保留所有#pragma编译器指令,因编译器要使用它们。
经过预编译后的文件不包含任何宏定义,因为所有的宏已展开,并且包含的文件也被包含到产生的文件中。
编译 :编译的过程就是把预编译后的文件进行一些词法分析、语义分析、及优化后产生的汇编文件。这个过程就是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。现在GCC把预编译和编译合成一个步骤。
汇编 :此过程是把汇编代码转变成机器码,因为每一个汇编语句几乎对应一条机器指令。所以汇编器的汇编过程相对于编译器来说比较简单,没有复杂的语法,也没有语义,也没有优化,只是根据汇编码和机器指令的对照表一一翻译就可以了。
链接 :此过程让人非常费解,为什么汇编器不一次性产生可执行的输出文件呢而产生许多目标文件?链接的过程到底包含了什么内容?为什么要链接?
编译器就是将高级语言编译成机器语言的一个工具。编译的过程一般是:扫描--语法分析--语义分析--源代码优化--代码生成--目标码优化。 我们在产生大的文件时,会产生多个模块的文件,这些文件通过接口相互引用。 如:我们A模块要调用B模块的一个动态数组或dll中的函数,在编译器A并不知道B中数组或函数的地址,如果这时就汇编中一个执行文件会读取内存出错。而只有在运行时即运行库中才能产生B中数组或函数的地址,这时A才能调用,所以这一步是在链接时完成的。
链接过程也把各模块“拼接”成一个可执行文件,所以链接过程变的十分重要和突出。