前言
C程序从源代码到可执行程序都经历了哪些过程?本文以Linux下C语言的编译过程为例,讲解C语言程序的编译过程。
例如:test.c是一个C语言源代码文件,我们常用如下命令编译和运行
$ gcc test.c -o test #编译
$ ./test #执行
上述gcc命令其实依次执行了四步操作:1.预处理(Preprocessing), 2.编译(Compilation), 3.汇编(Assemble), 4.链接(Linking)。
第一步:预处理(Preprocessing)
预处理是将所有的#include头文件里的内容拷贝过来,以及将所有的宏替换成#define宏定义的内容,预处理之后得到的仍然是文本文件,但文件体积会大很多。
gcc的预处理操作是由预处理器cpp来完成的,你可以通过如下命令对test.c进行预处理:
$ gcc -E test.c -o test.i -I../inc/ #-E是让编译器在预处理之后就退出,不进行后续编译过程;-o指定输出文件名;-I(大写的i)指定头文件所在路径。
或者直接调用cpp命令
$ cpp test.c -o test.i -I../inc/
经过预处理之后代码体积会大很多:
预处理前后 | 文件名 | 文件大小 | 代码行数 |
---|---|---|---|
预处理前 | test.c | 146B | 9 |
预处理后 | test.i | 17691B | 857 |
第二步:编译(Compilation)
这里的编译是指将经过预处理之后的程序转换成特定汇编代码(assembly code)的过程。编译的指定如下:
$ gcc -S test.c -o test.s -I../inc/ #-S让编译器在编译之后停止,不进行后续过程;-o指定输出文件名。
编译过程完成后,将生成程序的汇编代码test.s,这也是文本文件。
第三步:汇编(Assemble)
汇编过程将上一步的汇编代码转换成cpu能识别的机器码,这一步产生的文件叫做目标文件,是二进制格式。
gcc的汇编过程是由汇编器as来完成的,汇编命令如下:
gcc -c test.s -o test.o
或者直接调用as命令
$ as test.s -o test.o
这一步会为每一个源文件产生一个目标文件。即每个.c文件将生成一个.o文件
第四步:链接
链接过程将多个目标文以及所需的库文件(.so或.a等)链接成最终的可执行文件(executable file)。
gcc的链接过程是由链接器ld来完成的,链接命令如下:
$ ld test.o -o test -L. -lshare #-L是链接库的路径;-l是链接库的名字。
结语
经过以上分析,我们发现编译过程并不像想象的那么简单,而是要经过预处理、编译、汇编、链接。尽管我们平时使用gcc命令的时候没有关心中间结果,但每次程序的编译都少不了这几个步骤,所以你如果编译过后发现多了很多文件,这就是整个编译过程中生成的中间文件。