Cprogramming 简明教程
Compilation Process in C
C 语言是一种编译语言。与解释型语言相比,编译语言提供更快的执行性能。不同的编译器产品可用于编译 C 程序。它们有 GCC、Clang、MSVC 等。在本章中,我们将解释在使用 GCC 编译器编译 C 程序时后台发生的情况。
Compiling a C Program
由 1 和 0 位组成的二进制指令序列称为 machine code 。C、C++、Java 等高级编程语言包含更接近于英语等人类语言的关键字。因此,用 C(或任何其他高级语言)编写的程序需要转换为等效的机器代码。此过程称为 compilation 。
请注意,机器代码特定于硬件架构和操作系统。换句话说,在使用 Windows 操作系统计算机上编译的某个 C 程序的机器代码将与使用 Linux 操作系统计算机上的另一台计算机不兼容。因此,我们必须使用适合目标操作系统的编译器。
C Compilation Process Steps
在本次教程中,我们将使用 gcc(代表 GNU 编译器集合)。GNU 项目是理查德·斯托曼的一个自由软件项目,开发者可以免费使用该项目中的强大工具。
gcc 编译器支持多种编程语言,包括 C 语言。为了使用它,我们应该安装与目标计算机兼容的版本。
编译过程分为四步——
-
Preprocessing
-
Compiling
-
Assembling
-
Linking
下图展示了编译过程。
Example
为了理解这一过程,我们考虑用 C 语言编写的以下源代码:
#include <stdio.h>
int main(){
/* my first program in C */
printf("Hello World! \n");
return 0;
}
运行代码并检查其输出:
Hello World!
“.c” 是一个文件扩展名,通常表示文件是用 C 编写的。第一行是预处理器指令,它告诉编译器包含头文件。和之间的文本是注释,这些注释用于文档目的。
程序的入口点是。这意味着程序将从执行此函数块内的语句开始。这里,在给定的程序代码中,只有两个语句:一条将输出句子“Hello World”到终端,另一条语句告诉程序,如果退出或结束正确地,则“返回 0”。因此,一旦我们编译它,如果我们运行这个程序,我们只会看到“Hello World”这个短语出现。
What Goes Inside the C Compilation Process?
为了使我们的“main.c”代码可执行,我们需要输入命令“gcc main.c”,编译过程将经历它包含的所有四步。
Step 1: Preprocessing
预处理器执行以下操作——
-
它删除源文件中的所有注释。
-
它包括头文件(一个扩展名为 .h 的文件)的代码,该文件包含 C 函数声明和宏定义。
-
它用其值替换所有宏(已命名代码片段)。
这步的输出将存储在一个扩展名为“@”的文件中,因此这里它将在“@”中。
为了在此步骤后立即停止编译,我们可以在源文件中使用选项“@”与 gcc 命令,然后按 Enter。
gcc -E main.c
Step 2: Compiling
编译器从预处理过的文件中生成 IR 代码(中间表示),因此这将产生一个“.s”文件。话虽如此,其他编译器可能会在编译的这一步生成汇编代码。
我们可以通过 gcc 命令上的“@”选项在此步骤后停止,然后按 Enter。
gcc -S main.c
@ 文件应该是这样的——
.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
Step 3: Assembling
汇编器获取 IR 代码并将它转换成目标代码,即机器语言(二进制)代码。这将生成一个以“.o”结尾的文件。
我们可以在此步骤后通过使用带 gcc 命令的选项“-c”停止编译过程,然后按 Enter。
请注意,“main.o”文件不是文本文件,因此当你使用文本编辑器打开此文件时,其内容不可读。
Step 4: Linking
链接器创建最终可执行文件(二进制)。它将所有源文件的目标代码链接在一起。链接器知道在哪里查找@ 或 @ 中的函数定义。
静态库是编译器将所有使用的库函数复制到可执行文件的产物。动态库中的代码并没有全部复制,只有库的名称被放置在二进制文件中。
默认情况下,在这第四步也是最后一步后,也就是您在没有任何选项的情况下键入整个“ gcc main.c ”命令时,编译器将创建一个名为 main.out 的可执行程序(Windows 系统中的名称为 main.exe ),我们可以从命令行运行该程序。
我们还可以选择通过在 gcc 命令后添加“ -o ”选项(置于编译文件或多个文件的名称之后),来创建一个具有我们想要的名称的可执行程序。
gcc main.c -o hello.out
所以现在,如果您没有使用 "-o" 选项,我们可以键入“ ./hello.out ”;如果您使用了该选项,我们可以键入“ ./hello ”来执行编译的代码。输出将显示“ Hello World ”,然后将再次显示 shell 提示符。