通过前面的学习我们知道,根据源代码文件后缀名的不同,gcc 指令可以自行判断出该源程序是由哪种编程语言编写,从而完成由源代码到可执行代码的转换工作。考虑到本教程主要讲解
//存储在 demo.c 文件中 #include <stdio.h> int main(){ puts("GCC教程:http://c.biancheng.net/gcc/"); return 0; }无论是 C 还是 C++ 程序,其从源代码转变为可执行代码的过程,具体可分为 4 个过程,分别为预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。默认情况下,gcc 指令会一气呵成,直接将源代码历经这 4 个过程转变为可执行代码,且不会保留各个阶段产生的中间文件(可阅读《GCC编译C/C++程序(一步完成)》一节)。
而如果想查看这 4 个阶段各自产生的中间文件,最简单直接的方式就是对源代码进行“分步编译”,即控制 GCC 编译器逐步对源代码进行预处理、编译、汇编以及链接操作。其中,通过为 gcc 指令添加 -E 选项,即可控制 GCC 编译器仅对源代码做预处理操作。
值得注意的是,默认情况下 gcc -E 指令只会将预处理操作的结果输出到屏幕上,并不会自动保存到某个文件。因此该指令往往会和 -o 选项连用,将结果导入到指令的文件中。比如:所谓预处理操作,主要是处理那些源文件和头文件中以 # 开头的命令(比如 #include、#define、#ifdef 等),并删除程序中所有的注释 // 和 /* ... */。有关预处理操作的具体细节,读者可阅读《那些被编译器隐藏了的过程》一节做详细了解。
[root@bogon demo]# gcc -E demo.c -o demo.i
[root@bogon demo]# ls
demo.c demo.i
Linux 系统中通常用 ".i" 作为 C 语言程序预处理后所得文件的后缀名。由此,就完成了 demo.c 文件的预处理操作,并将其结果导入到了 demo.i 文件中。读者可自行去掉
-o demo.i
部分,查看该指令的执行结果。
读者可以通过执行
cat demo.i
指令查看该文件中的内容,但通常没有足够 C 语言功底的读者是看不懂的。为此,我们可以为 gcc 指令再添加一个 -C 选项,阻止 GCC 删除源文件和头文件中的注释:
[root@bogon demo]# gcc -E -C demo.c -o demo.i
注意,这里是大写的 -C,不是小写的 -c。小写的 -c 另作他用,后续章节会做详细讲解。
gcc -E支持的常用选项
除了 -C、-o 以外,根据实际场景的需要,gcc -E 后面还可以添加其它的选项,例如:-idirafter dir 都用于指定搜索头文件的目录,适用于以引号 "" 和 <> 导入的头文件。
其中,对于指定 #include 搜索路径的几个选项,作用的先后顺序如下:
- 对于用 #include "" 引号形式引入的头文件,首先搜索当前程序文件所在的目录,其次再前往 -iquote 选项指定的目录中查找;
- 前往 -I 选项指定的目录中搜索;
- 前往 -isystem 选项指定的目录中搜索;
- 前往默认的系统路径下搜索;
- 前往 -idirafter 选项指定的目录中搜索。
除表 1 罗列的几个选项之外,预处理过程可以使用的选项还有很多,比如 -imacros、-undef、-M 等,感兴趣的读者可前往官网 GCC10.1.0 预处理过程选项查看。