❮ 上一节
下一节 ❯
C 语言的编译过程
C 是一种编译型语言。与解释型语言相比,编译型语言提供更快的执行性能。编译 C 程序可以使用不同的编译器产品,例如 GCC、Clang、MSVC 等。本章将解释使用 GCC 编译器编译 C 程序时的后台运行情况。
编译 C 程序
由 1 和 0 位组成的二进制指令序列称为机器码。高级编程语言(例如 C、C++、Java 等)包含的关键字更接近人类语言(例如英语)。因此,用 C(或任何其他高级语言)编写的程序需要转换为等效的机器码。此过程称为编译。
请注意,机器码特定于硬件架构和操作系统。换句话说,在 Windows 操作系统的计算机上编译的某个 C 程序的机器码与在 Linux 操作系统的计算机上不兼容。因此,我们必须使用适合目标操作系统的编译器。
C 编译过程步骤
在本教程中,我们将使用 gcc(GNU 编译器集合)。 GNU 项目是由 Richard Stallman 发起的自由软件项目,旨在让开发者免费使用强大的工具。
gcc 编译器支持多种编程语言,包括 C 语言。为了使用它,我们需要安装与目标计算机兼容的版本。
编译过程分为四个不同的步骤 -
预处理
编译
汇编
链接
下图展示了编译过程。
示例
为了理解此过程,我们来看一下以下 C 语言源代码 (main.c) -
#include
int main(){
/* 我的第一个 C 语言程序 */
printf("Hello World!
");
return 0;
}
输出
运行代码并检查其输出 −
Hello World!
".c"是文件扩展名,通常表示该文件是用 C 语言编写的。第一行是预处理器指令 #include,指示编译器包含 stdio.h 头文件。/* 和 */ 中的文本是注释,用于文档编写。
该程序的入口点是 main() 函数。这意味着程序将从执行该函数块内的语句开始。此处,在给定的程序代码中,只有两条语句:一条在终端上打印"Hello World"语句,另一条语句指示程序在正确退出或结束时"返回 0"。因此,一旦我们编译完成,运行该程序时,我们只会看到"Hello World"这句话。
C 语言编译过程内部包含哪些内容?
为了使"main.c"代码可执行,我们需要输入命令"gcc main.c",编译过程将执行其包含的所有四个步骤。
步骤 1:预处理
预处理器执行以下操作:
它会删除源文件中的所有注释。
它会包含头文件的代码,头文件是一个扩展名为 .h 的文件,其中包含 C 函数声明和宏定义。
它会将所有宏(已命名的代码片段)替换为其值。
此步骤的输出将存储在一个文件中,其中包含扩展名为".i",因此它将位于"main.i"中。
为了在此步骤后立即停止编译,我们可以在源文件上使用 gcc 命令的"-E"选项,然后按 Enter 键。
gcc -E main.c
步骤 2:编译
编译器从预处理文件生成 IR 代码(中间表示),因此这将生成一个".s"文件。话虽如此,其他编译器可能会在此编译步骤生成汇编代码。
我们可以在此步骤后使用 gcc 命令中的"-S"选项停止,然后按 Enter。
gcc -S main.c
main.s 文件应该如下所示 -
.file "helloworld.c"
.text
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC0:
.ascii "Hello, World! \0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
call __main
leaq .LC0(%rip), %rcx
call puts
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"
.def puts; .scl 2; .type 32; .endef
步骤 3:汇编
汇编器获取 IR 代码并将其转换为目标代码,即机器语言代码(即二进制)。这将生成一个以".o"结尾的文件。
我们可以在 gcc 命令中使用选项"-c"并按 Enter 键来停止此步骤后的编译过程。
请注意,"main.o"文件不是文本文件,因此使用文本编辑器打开此文件时无法读取其内容。
步骤 4:链接
链接器会创建最终的二进制可执行文件。它将所有源文件的目标代码链接在一起。链接器知道在静态库或动态库中查找函数定义的位置。
静态库是链接器将所有用到的库函数复制到可执行文件的结果。动态库中的代码不会被完整复制,只有库的名称会被放入二进制文件中。
默认情况下,在第四步也是最后一步之后,即输入完整的"gcc main.c"命令而不使用任何选项时,编译器将创建一个名为"main.out"(在 Windows 系统中为"main.exe")的可执行程序,我们可以从命令行运行该程序。
我们也可以选择创建具有所需名称的可执行程序,方法是在 gcc 命令中添加"-o"选项,该选项位于我们正在编译的文件名之后。
gcc main.c -o hello.out
因此,如果您没有使用"-o""选项,那么现在我们可以输入"./hello.out"或者"./hello" 执行编译后的代码。输出将是"Hello World",随后会再次出现 shell 提示符。
❮ 上一节
下一节 ❯