在这篇文章中,我们介绍了如何使用GCC编译器编译C语言程序,以及预处理、编译、汇编和连接等不同阶段。
目录:
- GCC简介
- 编译
- 源代码示例
- 编译过程(4个步骤)
GCC简介
GCC是一个首字母缩写,代表GNU编译器集合。GCC是一个编译器和库的集合,为各种编程语言提供支持,如C、C++、Java等。
GCC编译器包含在大多数Linux发行版中。在这篇文章中,我们用Ubuntu做了所有的例子。
要检查它是否已经安装在你的系统中,运行下面的命令:
username@hostname:~$ gcc --version
OUTPUT
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
编译
编译程序是将源代码(人类可读的代码)转化为计算机可以理解的机器语言的过程。
编译器是一个计算机程序,它将用高级编程语言(如C)编写的源代码翻译成低级语言,如机器代码。
源代码示例
我们使用Vim作为我们的编辑器。
创建一个新文件并命名为hello.c。.c
文件的扩展名表示该文件包含用C语言编写的源代码。
username@hostname:~$ vim hello.c
源代码
hello.c
1
2 #include <stdio.h>
3
4 int main() {
5 printf("Hello World!\n");
6 return 0;
7 }
8
编译过程
在编译过程中,有四个主要步骤,如下所述:
- 预处理
- 编译
- 汇编
- 链接
我们用一个基本的hello world程序来说明编译过程是如何发生的。
1.预处理程序
预处理程序执行以下任务。
- 它删除源文件中的所有注释,并用一个空格来代替它们。
- 它包括头文件中的代码。
- 它将预定义的宏名替换为其扩展名。
GCC编译器有几个可用的选项,可以在不同步骤后停止编译。
要查看预处理阶段的输出,我们可以使用命令gcc -E program_name.c选项,如下图所示:
username@hostname:~$ gcc -E hello.c
预处理程序的输出被发送到终端。要保存输出,请运行以下命令:
username@hostname:~$ gcc -E hello.c > hello.i
.i文件的扩展名表示该文件是一个预处理文件。下面的代码是hello.i文件的一部分。
要查看该文件,请运行:
username@hostname:~$ vim hello.i
OUTPUT
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 858 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 873 "/usr/include/stdio.h" 3 4
# 3 "hello.c" 2
# 4 "hello.c"
int main() {
printf("Hello World");
return 0;
}
2.编译器
在这个阶段,编译器接收预处理后的文件,并根据编译器的情况生成IR(Intermediate Representation)代码或汇编代码。
要在这一步之后停止编译过程,我们可以使用gcc -S program_name.c选项的命令,如下所示:
username@hostname:~$ gcc -S hello.c
该命令创建了一个包含汇编代码的hello.s文件。
要查看该文件,请运行:
username@hostname:~$ vim hello.s
OUTPUT
.file "hello.c"
.text
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
3.汇编器
在第三阶段,汇编器将汇编代码翻译成可执行的机器代码。
要在这一步之后停止编译过程,我们可以使用gcc -S program_name.c选项,如下所示。
username@hostname:~$ gcc -c hello.c
该命令创建了一个包含机器代码的hello.o文件,该文件不是人类可读的。
要查看该文件,请运行:
username@hostname:~$ vim hello.o
OUTPUT
^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^X^C^@^@^@^@^@^@^@^@^@^@@^@^@^@^@^@@^@^N^@^M^@ó^O^^úUH<89>åH<8d>=^@^@^@^@¸^@^@^@^@è^@^@^@^@¸^@^@^@^@]ÃHello World^@^@GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0^@^@^D^@^@^@^P^@^@^@^E^@^@^@GNU^@^B^@^@À^D^@^@^@^C^@^@^@^@^@^@^@^T^@^@^@^@^@^@^@^AzR^@^Ax^P^A^[^L^G^H<90>^A^@^@^\^@^@^@^\^@^@^@^@^@^@^@ ^@^@^@^@E^N^P<86>^BC^M^FW^L^G^H^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^A^@^@^@^D^@ñÿ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^A^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^C^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^D^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^E^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^G^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@^H^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^C^@
4.链接器
链接器执行的任务包括。
- 将来自不同源文件的所有对象文件连接起来。
- 将函数调用与它们的定义联系起来。链接器知道函数定义在静态或动态库中的位置。
为了完成编译过程,我们有两个选择:
选项1
运行下面的命令:
username@hostname:~$ gcc hello.c
当你运行这个命令时,编译器会生成一个名为a.out的可执行程序。要运行这个可执行程序,请输入以下命令:
username@hostname:~$ ./a.out
选项2
要给可执行程序一个不同的名字,我们可以在gcc命令中的编译文件名后面加上"-o "选项,如下所示:
username@hostname:~$ gcc hello.c -o helloprogram
要运行该可执行程序,请使用下面的命令:
username@hostname:~$ ./helloprogram
通过OpenGenus的这篇文章,你一定对如何使用GCC编译C程序有了深刻的认识。