1.1 GCC编译C/C++的四个过程
gcc 是 GUN Compiler Collection的缩写。
- 预处理(pre-processing)E:插入头文件,替换宏
- 编译(Compiling)S:编译成汇编
- 汇编(Assembling) c:编译成目标文件
- 链接 (Linking):链接到库中,变成可执行文件
gcc -E main.c -o main.i
gcc -S main.i –o main.s
gcc –c main.s –o main.o
gcc main.o –o main
:链接,生成可执行文件
./main
也可以一次性完成:
gcc main.c –o main
但一般情况下生成.o文件比较好,可以重定位文件,让别人使用
1.1.1 预处理
C语言代码在交给编译器之前,会先由预处理器进行一些文本替换方面的操作,例如宏展开、文件包含、删除部分代码等。
在正常的情况下,GCC 不会保留预处理阶段的输出文件,也即 .i 文件。然而,可以利用 -E 选项保留预处理器的输出文件,以用于诊断代码。-E选项指示 GCC 在预处理完毕之后即可停止。
a、默认情况下,预处理器的输出会被导入到标准输出流(也就是控制台)
gcc -E main.c
b、可以利用-o选项把它导入到某个输出文件
gcc -E main.c -o main.i
或者
gcc -E main.c > main.i
表示把预处理的结果导出到 main.i
文件中。
c、使用-C
选项阻止预处理器删除源文件和头文件中的注释。
注意,这里是大写的 -C,不是小写的 -c。小写的 -c 表示只编译不链接。
gcc -E -C main.c -o main.i
对比之前的输出,可以发现,保留愿文件中的注释
1.1.2 编译阶段
编译器的核心任务是把C程序翻译成机器的汇编语言(assembly language)。
汇编语言是人类可以阅读的编程语言,也是相当接近实际机器码的语言。每种 CPU 架构都有不同的汇编语言。
实际上,GCC 是一个适合多种 CPU 架构的编译器,不会把C程序语句直接翻译成目标机器的汇编语言,而是在输入语言和输出汇编语言之间,利用一个中间语言,称为 RegisterTransfer Language(简称 RTL,寄存器传输语言)。借助于这个抽象层,在任何背景下,编译器可以选择最经济的方式对给定的操作编码。
通常情况下,GCC 把汇编语言输出存储到临时文件中,并且在汇编器执行完后立刻删除它们。但是可以使用-S选项,让编译程序在生成汇编语言输出之后立刻停止,而不删除临时文件。
-S可以理解为Save
或者Stop
,自己的理解仅供参考记忆。
如果没有指定输出文件名,那么采用-S
选项的 GCC 编译过程会为每个被编译的输入文件生成以.s作为后缀的汇编语言文件。如下例所示:
gcc -S ts.c
编译器预处理 ts.c
,将其翻译成汇编语言,并将结果存储在 ts.s
文件中,可以用文本编辑器察看.
指定输出文件名
gcc -S main.i -o filename.s
1.1.3 汇编阶段
-c
选项:只编译不链接,仅生成目标文件。
目标文件是一种中间文件或者临时文件,如果不设置该选项,gcc 一般不会保留目标文件,可执行文件生成完成后就自动删除了。
注意,使用-c选项表示只编译源文件,而不进行链接,因此,对于链接中的错误是无法发现的。
比如,调用一个不存在的函数,编译器会提示的错误。但是该命令不会输出错误信息。
gcc -c main.s -o main.o
1.1.4 链接阶段
gcc main.o -o main.exe
1.2 clang 分四步编译main.c
这里用的clang/clang++ 分四步编译main.c/main.cpp文件
1.2.1 预处理
clang++ -E main.cpp -o main.ii
1.2.2 编译阶段,生成汇编
clang++ -S main.ii -o main.s
1.2.3 汇编阶段,生成目标文件
clang++ -c main.s -o mian.o
1.2.4 连接阶段
clang++ mian.o -o main
1.2.5 执行
./main
1.2.6 源文件
#include
int main() {
std::cout << "Hello Biter !" << std::endl;
return 0;
}
1.2.7 四部曲之一步到胃
clang++在编译的过程中,保存所有编译过程中产生的文件
1.2.8 产生中间文件
clang++ -save-temps main.cpp -o main
1.2.9 不保存中间文件
clang++ main.cpp -o main