目前很多IDE(集成开发环境)把编译过程封装的很好,导致很多嵌入式软件工程师不知道一个C程序的具体的编译过程,这一章我们来讲讲C程序的编译过程。
1. GCC编译过程(精简版)
如下图所示,一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)和链接(linking)等4步才能变成可执行文件。在日常交流中通常使用“编译”统称这4个步骤.
通过不同的gcc选项可以控制这些过程:
在日常交流中通常使用“编译”统称这4个步骤,如果不是特指这4个步骤中的某一个,本教程也依惯例使用“编译”这个统称。
gcc使用示例:
gcc hello.c // 输出一个名为a.out的可执行程序,然后可以执行./a.out
gcc -o hello hello.c // 输出名为hello的可执行程序,然后可以执行./hello
gcc -o hello hello.c -static // 静态链接
gcc -c -o hello.o hello.c // 先编译(不链接)
gcc -o hello hello.o // 再链接
2. 预处理
2.1 预处理含义
故名思意就是预先处理,其中处理了以下几部分:
- 展开包含的头文件,即#include后包含的头文件,全部复制到当前程序展开。
- #define定义的符号替换
- 进行条件编译
- 删除所有注释
2.2 预处理实验
使用GCC编译器,可以通过加上-E选项指定只进行预处理过程。如
gcc -E -o hello.i hello.c
-o 选项表示指定输出文件,上述语句表示将hello.c文件只进行预处理,生成hello.i文件(.i后缀为预处理过后的文件格式)
下图左边为.C文件,右边为.i部分文件,可见首先将头文件stdio.h进行展开,由于stdio.h包含了其他文件,所以该展开会进行递归,将所有涉及的文件都展开。
如下图所示,预处理后的文件将宏定义的MAX和MIN进行了替换
如下图所示,预处理还会进行条件编译,如果条件为假,那么用空行替换相应的语句
下图注意到注释使用了空行进行替换(一行注释使用空行,如果是部分注释则使用空格进行替换)
2. 编译
2.1 含义
编译过程是将源文件编译为将C语言代码转换成汇编代码,还有一些更复杂的操作。
2.2 实验
使用GCC编译器,可以通过加上-E选项指定只进行预处理过程。如
gcc -S -o hello.s hello.i
生成的汇编文件如下图所示
3. 汇编
3.1 含义
主要将汇编代码转换成二进制指令(机器指令)
3.2 实验
使用GCC编译器,可以通过加上-c选项指定只进行预处理过程。如
gcc -c -o hello.o hello.s
生成的二进制文件如下图所示(使用文本编辑器打开会出现乱码)
4. 链接
4.1 含义
将所有的二进制文件(.o文件)进行链接,生成可执行文件.elf