编译器

在 C 预处理器包含所有头文件并扩展所有宏之后,编译器可以编译该程序。它通过将 C 源代码转换为目标代码文件来实现,目标代码文件是以 .o 结尾的文件,其中包含源代码的二进制版本。但是,对象代码不能直接执行。为了生成可执行文件,你还必须为文件中的所有库函数添加代码(这与包含声明不同,这是 #include 的作用)。这是链接器的工作。

通常,如何调用 C 编译器的确切顺序在很大程度上取决于你使用的系统。这里我们使用的是 GCC 编译器,但需要注意的是存在更多的编译器:

% gcc -Wall -c foo.c

%是操作系统的命令提示符。这告诉编译器在文件 foo.c 上运行预处理器,然后将其编译到目标代码文件 foo.o 中。-c 选项意味着将源代码文件编译为目标文件,但不调用链接器。此选项 -c 可在 POSIX 系统上使用,例如 Linux 或 macOS; 其他系统可能使用不同的语法。

如果你的整个程序都在一个源代码文件中,你可以改为:

% gcc -Wall foo.c -o foo

这告诉编译器在 foo.c 上运行预处理器,编译它然后链接它以创建一个名为 foo 的可执行文件。-o 选项表明该行的下一个单词是二进制可执行文件(程序)的名称。如果你没有指定 -o,(如果你只输入 gcc foo.c),由于历史原因,可执行文件将被命名为 a.out

通常,将 .c 文件转换为可执行文件时,编译器会执行以下四个步骤:

  1. 预处理 - 在 .c 文件中以文本方式展开 #include 指令和 #define
  2. 编译 - 将程序转换为程序集(你可以通过添加 -S 选项在此步骤停止编译器)
  3. assembly - 将程序集转换为机器代码
  4. linkage - 将目标代码链接到外部库以创建可执行文件

另请注意,我们使用的编译器的名称是 GCC,它代表“GNU C 编译器”和“GNU 编译器集合”,具体取决于上下文。存在其他 C 编译器。对于类 Unix 操作系统,其中许多都有名称 cc,用于“C 编译器”,它通常是一些其他编译器的符号链接。在 Linux 系统上,cc 通常是 GCC 的别名。在 macOS 或 OS-X 上,它指向 clang。

POSIX 标准目前要求 c99 作为 C 编译器的名称 - 它默认支持 C99 标准。早期版本的 POSIX 强制要求 c89 作为编译器。POSIX 还要求此编译器理解我们上面使用的 -c-o 选项。

注意: gcc 示例中的 -Wall 选项告诉编译器打印有关可疑结构的警告,强烈建议这样做。添加其他警告选项也是一个好主意,例如 -Wextra