C 是一种著名的编程语言,深受经验丰富和新手程序员的欢迎。用 C 语言编写的源代码使用标准英语术语,因此被认为是人类可读的。但是,计算机只能理解二进制代码。要将代码转换为机器语言,您需要使用称为编译器的工具。
GCC(GNU C 编译器)是一种非常常见的编译器。编译过程涉及几个中间步骤和相邻工具。
安装 GCC
要确认您的系统是否已安装 GCC,请使用 gcc
命令
$ gcc --version
如果需要,请使用您的软件包管理器安装 GCC。在基于 Fedora 的系统上,使用 dnf
$ sudo dnf install gcc libgcc
在基于 Debian 的系统上,使用 apt
$ sudo apt install build-essential
安装后,如果您想查看 GCC 的安装位置,请使用
$ whereis gcc
使用 GCC 的简单 C 程序
这是一个简单的 C 程序,演示如何使用 GCC 编译代码。打开您喜欢的文本编辑器并粘贴以下代码
// hellogcc.c
#include <stdio.h>
int main() {
printf("Hello, GCC!\n");
return 0;
}
将文件另存为 hellogcc.c
,然后编译它
$ ls
hellogcc.c
$ gcc hellogcc.c
$ ls -1
a.out
hellogcc.c
如您所见,a.out
是编译生成的默认可执行文件。要查看新编译的应用程序的输出,只需像运行任何本地二进制文件一样运行它
$ ./a.out
Hello, GCC!
命名输出文件
文件名 a.out
不是很具描述性,因此如果您想为可执行文件指定特定名称,可以使用 -o
选项
$ gcc -o hellogcc hellogcc.c
$ ls
a.out hellogcc hellogcc.c
$ ./hellogcc
Hello, GCC!
当开发需要编译多个 C 源文件的大型应用程序时,此选项非常有用。
GCC 编译的中间步骤
即使 GCC 在简单用例中自动执行编译,但实际上编译有四个步骤。
- 预处理:GNU C 预处理器 (
cpp
) 解析头文件(#include 语句),展开宏(#define 语句),并生成一个中间文件,例如hellogcc.i
,其中包含展开的源代码。 - 编译:在此阶段,编译器将预处理的源代码转换为特定 CPU 架构的汇编代码。生成的汇编文件以
.s
扩展名命名,例如本例中的hellogcc.s
。 - 汇编:汇编器 (
as
) 将汇编代码转换为目标文件中的机器代码,例如hellogcc.o
。 - 链接:链接器 (
ld
) 将目标代码与库代码链接,以生成可执行文件,例如hellogcc
。
运行 GCC 时,使用 -v
选项可以查看每个步骤的详细信息。
$ gcc -v -o hellogcc hellogcc.c

(Jayashree Huttanagoudar,CC BY-SA 4.0)
手动编译代码
体验编译的每个步骤可能很有用,因为在某些情况下,您不需要 GCC 完成所有步骤。
首先,删除当前文件夹中 GCC 生成的文件,但源文件除外。
$ rm a.out hellogcc.o
$ ls
hellogcc.c
预处理器
首先,启动预处理器,将其输出重定向到 hellogcc.i
$ cpp hellogcc.c > hellogcc.i
$ ls
hellogcc.c hellogcc.i
查看输出文件,并注意预处理器如何包含头文件和展开宏。
编译器
现在您可以将代码编译为汇编代码。使用 -S
选项将 GCC 设置为仅生成汇编代码。
$ gcc -S hellogcc.i
$ ls
hellogcc.c hellogcc.i hellogcc.s
$ cat hellogcc.s
查看汇编代码,了解生成了什么。
汇编
使用您刚刚生成的汇编代码创建目标文件
$ as -o hellogcc.o hellogcc.s
$ ls
hellogcc.c hellogcc.i hellogcc.o hellogcc.s
链接
要生成可执行文件,您必须将目标文件链接到它所依赖的库。这不像之前的步骤那么容易,但它具有教育意义
$ ld -o hellogcc hellogcc.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: hellogcc.o: in function `main`:
hellogcc.c:(.text+0xa): undefined reference to `puts'
在链接器完成查看 libc.so
库后,会出现引用 undefined puts
的错误。您必须找到合适的链接器选项来链接所需的库以解决此问题。这不是一件容易的事,它取决于您的系统的布局方式。
链接时,您必须将代码链接到核心运行时 (CRT) 对象,这是一组帮助二进制可执行文件启动的子例程。链接器还需要知道在哪里找到重要的系统库,包括 libc 和 libgcc,尤其是在特殊的开始和结束指令中。这些指令可以使用 --start-group
和 --end-group
选项或使用 crtbegin.o
和 crtend.o
的路径来分隔。
此示例使用 RHEL 8 安装中出现的路径,因此您可能需要根据您的系统调整路径。
$ ld -dynamic-linker \
/lib64/ld-linux-x86-64.so.2 \
-o hello \
/usr/lib64/crt1.o /usr/lib64/crti.o \
--start-group \
-L/usr/lib/gcc/x86_64-redhat-linux/8 \
-L/usr/lib64 -L/lib64 hello.o \
-lgcc \
--as-needed -lgcc_s \
--no-as-needed -lc -lgcc \
--end-group
/usr/lib64/crtn.o
Slackware 上的相同链接器过程使用一组不同的路径,但您可以看到过程的相似之处
$ ld -static -o hello \
-L/usr/lib64/gcc/x86_64-slackware-linux/11.2.0/ \
/usr/lib64/crt1.o /usr/lib64/crti.o \
hello.o /usr/lib64/crtn.o \
--start-group -lc -lgcc -lgcc_eh \
--end-group
现在运行生成的可执行文件
$ ./hello
Hello, GCC!
一些有用的实用程序
以下是一些实用程序,可帮助检查文件类型、符号表以及与可执行文件链接的库。
使用 file
实用程序确定文件类型
$ file hellogcc.c
hellogcc.c: C source, ASCII text
$ file hellogcc.o
hellogcc.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ file hellogcc
hellogcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb76b241d7d00871806e9fa5e814fee276d5bd1a, for GNU/Linux 3.2.0, not stripped
使用 nm
实用程序列出目标文件的符号表
$ nm hellogcc.o
0000000000000000 T main
U puts
使用 ldd
实用程序列出动态链接库
$ ldd hellogcc
linux-vdso.so.1 (0x00007ffe3bdd7000)
libc.so.6 => /lib64/libc.so.6 (0x00007f223395e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2233b7e000)
总结
在本文中,您了解了 GCC 编译的各种中间步骤以及用于检查文件类型、符号表和与可执行文件链接的库的实用程序。下次您使用 GCC 时,您将了解它生成二进制文件的步骤,并且当出现问题时,您知道如何逐步解决问题。
评论已关闭。