C程序的编译过程

本文讨论了C程序的完整编译过程,分别讲述了预处理、编译、汇编、链接各阶段完成的编译任务。然后通过一个编译实例,探讨了各阶段输出的文件。

编译和链接


前面了解C程序结构,也编写了第一个C程序源代码,但计算机并不能直接执行C程序源代码。因为C语言是高级语言,计算机不能识别高级语言编写的代码,还需要把写好的C程序源代码转换为计算机能识别的机器语言代码,这个过程称为C程序的编译过程。编译过程如图1-7所示。

C程序编译过程

C程序编译过程

从上图可以看出,C程序编译过程分为四个阶段:第一阶段是预处理阶段;第二阶段是编译阶段;第三阶段是汇编阶段;第四阶段是链接阶段。执行这四个阶段的程序(预处理器、编译器、汇编器、和链接器)一起构成了C语言的编译系统。
预处理阶段:预处理阶段主要用于处理源文件中的预处理指令,并根据指令对源代码进行修改和补充。例如:“#include”指令将头文件的内容补充到源文件内;“#define”指令用于宏定义,通过宏定义可以进行条件编译和内容替换。
编译阶段:编译阶段主要用于对预处理后的文件进行词法和语法分析,并将其翻译成与源代码等价的汇编代码。
汇编阶段:汇编阶段将编译输出的汇编代码转换为机器代码,机器代码也称为目标代码,该代码与计算机硬件相关。
链接阶段:链接阶段将多个目标代码文件、函数库文件链接成可执行文件。

编译实例

下面以《C程序结构》中的面积计算器程序为例,在Ubuntu系统下使用GCC编译器,对面积计算器程序进行编译和链接。
面积计算器程序可以计算长方形、正方形、平行四边形和三角形的面积,它由main.c、rectangle.c、square.c、paraller.c、triangle.c、area.h五个源文件和一个头文件构成,area.h头文件描述了面积计算函数的原型。面积计算器程序结构如图所示。程序代码请参见《C程序结构》。

面积计算器程序构成

面积计算器程序构成

预处理阶段


预处理阶段主要处理C程序源文件内的预处理指令,如#include指令、宏定义等。预处理阶段会对所有的C源文件进行预处理,预处理后的文件也是文本文件,可以使用文本编辑器打开,不过其文件大小要比C源文件大的多,因为预处理后的文件会将#include指令引入的头文件写入到C源文件内。面积计算器的main.c文件使用了#include指令,主要代码如下:

//引入C语言标准输入输出函数库
//stdio.h是标准输入输出函数库的头文件
#include<stdio.h>
//引入面积计算器函数头文件
include "area.h"
void main()
{
……
}

GCC编译器提供了如下命令对C源文件进行预处理:
gcc -E C源文件 -o [预处理后文件]
例如:

gcc -E main.c -o main.i

对main.c文件进行预处理,预处理后的文件为main.i。执行上述预处理命令时,需要gcc能够找到main.c文件。
main.c预处理后文件main.i要比main.c大很多,感兴趣的同学可以实际操作后,用文本编辑器打开main.i文件,看看里面的内容。


面积计算器程序所有C源文件预处理后的文件列表,从列表中可以看出,除了main.c文件外,其它预处理后文件和C源文件大小变化不大,这是因为这些C源文件没有包含头文件。


编译阶段


编译阶段将预处理后文件转换为汇编代码,编译后的汇编代码也是文本文件,可以直接使用文本编辑器打开汇编代码。
GCC编译器提供了如下命令对预处理后文件进行编译:
gcc -E 预处理后文件 -o [汇编代码]
例如:

gcc -S square.i -o square.s

对square.i文件进行编译,编译后的文件为square.s。执行上述预处理命令时,需要gcc能够找到square.i文件。
编译后汇编代码如下图所示:


汇编阶段


汇编阶段将汇编代码转换为机器代码(机器可以直接执行的CPU指令),转换程序也称为汇编程序。由于不同CPU架构的计算机,其提供的指令集不同,支持运行的机器代码也不同,因此,要运行在不同CPU架构的计算机上,就需要运行支持该CPU架构的汇编程序。例如IBM-PC机采用了Intel X86系列处理器,单片机一般采用ARM架构的系列处理器,如果要让编写的汇编程序运行在IBM-PC机上,就需要调用支持IBM-PC指令集的汇编程序,如果要让编写的汇编程序运行在ARM架构的计算机上,就需要调用支持ARM指令集的汇编程序。
汇编阶段输出的文件为目标文件,它是二进制格式,不能使用文本编辑器浏览文件内容。GCC编译器提供了如下命令对C源文件进行预处理:
gcc -c 汇编代码文件 -o [目标文件]
例如:

gcc -c rectangle.s -o rectangle.o

对rectangle.s文件进行汇编,汇编后的文件为rectangle.o。执行上述预处理命令时,需要gcc能够找到rectangle.s文件。
汇编阶段执行后,面积计算器程序文件列表如下图所示:


链接阶段


面积计算器程序在汇编阶段输出了main.o、square.o、triangle.o、rectangle.o、paraller.o目标文件,在链接阶段需要将这些目标文件链接为可执行文件。链接使用ld命令,ld是GCC提供的链接器,将目标文件与函数库链接为可执行程序或库文件。不过ld链接器一般不独立使用,由GCC自动调用链接器。
输入下面的命令:
gcc main.c paraller.c rectangle.c square.c triangle.c
即可完成面积计算器程序的编译和链接。

本文小结


通过以上分析可知,C程序的编译过程要经过预处理、编译、汇编和链接四个阶段。令人欣慰的是C程序编译工具都提供了一次性编译和链接命令,编译器会自动执行这四个阶段,不需要开发者的参与。