1.第一步首先是对源程序进行预编译,预编译的过程比较简单,主要做如下的事:
1)将开头的宏定义如#define全部删除,并在程序中出现该宏的位置使用宏值替代
2)处理预编译指令和条件预编译指令,例如如果出现了#include<XX文件>,编译器会找到XX文件并将其全部插入到原include语句的位置处
3)删除所有的注释;添加行号和文件名标识等。
预编译的指令是:
gcc -E main.c -o main.i #其中-E表示对main.c源程序进行预编译,-o后面接输出文件名,这里以.i为后缀名标识预编译文件
2.第二步是对预编译过的程序进行编译,即进行词法分析、语法分析、语义分析及优化之后生成相应的汇编代码文件(就是编译原理这门课程讲的内容)。这是整个程序构建的核心部分和最复杂的部分。这里不展开解释过程,想了解的话去学习编译原理即可,下面给出进行编译的指令:
gcc -S main.i -o main.s #-S表示进行编译
前两个步骤生成的结果其实还是指令,我们在Linux中可以用cat命令查看其内容,后两步生成的都是二进制可执行文件类型了,直接查看会乱码。
3.第三步,对优化好的汇编代码进行翻译,将汇编指令逐一翻译成CPU可识别的机器指令。命令如下:
gcc -c main.c -o main.o
本次步骤生成的代码文件其实已经是二进制可执行文件格式了,但是可以看见后缀名是.o格式,并不是.bin格式,原因是这里该步骤得到的变量或函数的地址还没有最终确定,只是相对于文件开头的逻辑地址而已,还不能放到CPU上去执行。还需要进行重定位,即重新编排地址,理由很简单,我们经常在一个程序文件中调用另一个程序文件中的某个函数或者变量,而我们在编译该文件时还没有得到需要调用的变量或函数,所以地址也就不能确定。这需要在最后一步链接的过程综合所有程序文件确定最终的地址。.o文件也称为目标文件。
4. 最后一步就是进行链接,也就是最终确定程序中函数或变量的地址,使其可以去处理器上运行,执行该步骤后生成的就是相应平台上真正的可执行文件了。执行该步骤可以直接用gcc得到,也可以使用ld命令手动链接。