# 3.2 程序编码
假设一个 C 程序,有两个文件 p1.c 和 p2.c。我们用 Unix 命令行编译这些代码:
linux> gcc -Og -o p p1.c p2.c
命令 gcc 指的就是 GCC C 编译器。因为这是 Linux 上默认的编译器,我们也可以简单地用 cc 来启动它。编译选项 -Og1 告诉编译器使用会生成符合原始 C 代码整体结构的机器代码的优化等级。使用较高级别优化产生的代码会严重变形,以至于产生的机器代码和初始源代码之间的关系非常难以理解。因此我们会使用 -Og 优化作为学习工具,然后当我们增加优化级别时,再看会发生什么。实际中,从得到的程序的性能考虑,较高级别的优化(例如,以选项 -O1 或 -O2 指定)被认为是较好的选择。
实际上 gcc 命令调用了一整套的程序,将源代码转化成可执行代码。首先,C 预处理器扩展源代码,插入所有用 #include 命令指定的文件,并扩展所有用 #define 声明指定的宏。其次,编译器产生两个源文件的汇编代码,名字分别为 p1.s 和 p2.s。接下来,汇编器会将汇编代码转化成二进制目标代码文件 p1.o 和 p2.o。目标代码是机器代码的一种形式,它包含所有指令的二进制表示,但是还没有填入全局值的地址。最后,链接器将两个目标代码文件与实现库函数(例如 printf)的代码合并,并产生最终的可执行代码文件 p(由命令行指示符 -o p 指定的)。可执行代码是我们要考虑的机器代码的第二种形式,也就是处理器执行的代码格式。我们会在第 7 章更详细地介绍这些不同形式的机器代码之间的关系以及链接的过程。
1 GCC 版本 4.8 引入了这个优化等级。较早的 GCC 版本和其他一些非 GNU 编译器不认识这个选项。对这样一些编译器,使用一级优化(由命令行标志 -O1 指定)可能是最好的选择,生成的代码能够符合原始程序的结构。
← 3.1 历史观点 3.2.1 机器级代码 →