GCC工具介绍以及常见的用法;
GCC编译命令:
1. GCC工具
GCC编译器:
GCC(GNU Compiler Collection)是由 GNU 开发的编程语言编译器。 GCC最初代表“GNU C Compiler”,当时只支持C语言。 后来又扩展能够支持更多编程语言,包括 C++、Fortran 和 Java 等。 因此,GCC也被重新定义为“GNU Compiler Collection”,成为历史上最优秀的编译器, 其执行效率与一般的编译器相比平均效率要高 20%~30%。
GCC的官网地址为:https://gcc.gnu.org/,在Ubuntu系统下系统默认已经安装好GCC编译器,可以通过如下命令查看Ubuntu系统中GCC编译器的版本及安装路径:

GCC编译工具链:
GCC编译工具链(toolchain),是指以GCC编译器为核心的一整套工具。它主要包含以下三部分内容:
- gcc-core:即GCC编译器,用于完成预处理和编译过程,把C代码转换成汇编代码。
- Binutils :除GCC编译器外的一系列小工具包括了链接器ld,汇编器as、目标文件格式查看器readelf等。
- glibc:包含了主要的 C语言标准函数库,C语言中常常使用的打印函数printf、malloc函数就在glibc 库中。
在很多场合下会直接用GCC编译器来指代整套GCC编译工具链。
Binutils工具集:
Binutils(bin utility),是GNU二进制工具集,通常跟GCC编译器一起打包安装到系统,它的官方说明网站地址为: https://www.gnu.org/software/binutils/ 。
在进行程序开发的时候通常不会直接调用这些工具,而是在使用GCC编译指令的时候由GCC编译器间接调用。下面是其中一些常用的工具:
- as:汇编器,把汇编语言代码转换为机器码(目标文件)。
- ld:链接器,把编译生成的多个目标文件组织成最终的可执行程序文件。
- readelf:可用于查看目标文件或可执行程序文件的信息。
- nm : 可用于查看目标文件中出现的符号。
- objcopy: 可用于目标文件格式转换,如.bin 转换成 .elf 、.elf 转换成 .bin等。
- objdump:可用于查看目标文件的信息,最主要的作用是反汇编。
- size:可用于查看目标文件不同部分的尺寸和总尺寸,例如代码段大小、数据段大小、使用的静态内存、总大小等。
系统默认的Binutils工具集位于/usr/bin目录下,可使用如下命令查看系统中存在的Binutils工具集:
1 | # 在Ubantu上执行如下命令 |

glibc库:
glibc库是GNU组织为GNU系统以及Linux系统编写的C语言标准库,因为绝大部分C程序都依赖该函数库,该文件甚至会直接影响到系统的正常运行,例如常用的文件操作函数read、write、open,打印函数printf、动态内存申请函数malloc等。
在Ubuntu系统下,libc.so.6是glibc的库文件,可直接执行该库文件查看版本,在主机上执行如下命令:
1 | # 在Ubantu上执行如下命令 |

2. GCC编译
编写HelloWorld文件:
1 |
|
编译并执行:
Ubuntu默认安装GCC编译工具链,写好程序后可以直接进行编译,执行以下命令:
1 | # 在Ubantu的hello_c目录下执行如下命令 |
GCC 编译工具链在编译一个C源文件时需要经过以下 4 步:
- 预处理:为把头文件的代码、宏之类的内容转换成生成的.i文件,还是C代码。
- 编译:把预处理后的.i文件通过编译成.s文件,汇编语言。
- 汇编:将汇编语言文件生成目标文件.o文件,机器码。
- 链接:将每个源文件对应的.o文件链接起来,就生成一个可执行程序文件。

(1)预处理阶段
预处理过程中,对源代码文件中的文件包含 (include)、 预编译语句 (如宏定义define等)进行展开,生成 .i 文件。 可理解为把头文件的代码、宏之类的内容转换成更纯粹的C代码,不过生成的文件以.i为后缀。
使用GCC的参数 “-E”,可以让编译器生成 .i 文件,参数 “-o”,可以指定输出文件的名字。
具体命令如下:
1 | # 预处理,可理解为把头文件的代码汇总成C代码,把*.c转换得到*.i文件 |
编译生成的hello.i文件内容如下:

相当于它把原C代码中包含的头文件中引用的内容汇总到一处, 如果原C代码有宏定义,它把宏定义展开成具体的内容。
(2)编译阶段
把预处理后的.i文件通过编译成为汇编语言,生成.s文件,即把代码从C语言转换成汇编语言,这是GCC编译器完成的工作。在这个过程,GCC会检查各个源文件的语法,即使我们调用了一个没有定义的函数,也不会报错。
GCC可以使用-S选项,让编译程序生成汇编语言的代码文件(.s后缀)。
具体命令如下:
1 | # 编译,可理解为把C代码转换为汇编代码,把*.i转换得到*.s文件 |
编译生成的hello.s文件内容如下:

汇编语言是跟平台相关的,由于本示例的GCC目标平台是x86,所以此处生成的汇编文件是x86的汇编代码。
(3)汇编阶段
将汇编语言文件经过汇编,生成目标文件.o文件,每一个源文件都对应一个目标文件。即把汇编语言的代码转换成机器码,这是as汇编器完成的工作。
GCC的参数“c”表示只编译(compile)源文件但不链接,会将源程序编译成目标文件(.o后缀)。计算机只认识0或者1,不懂得C语言,也不懂得汇编语言,经过编译汇编之后,生成的目标文件包含着机器代码,这部分代码就可以直接被计算机执行。一般情况下,可以直接使用参数“c”,跳过上述的两个过程,具体命令 如下:
1 | # 汇编,可理解为把汇编代码转换为机器码,把*.s转换得到*.o,即目标文件 |
Linux下生成的 .o目标文件、so动态库文件以及下一小节链接阶段生成最终的可执行文件都是elf格式的, 可以使用“readelf”工具来查看它们的内容。
请试执行如下命令:

从 readelf 的工具输出的信息,可以了解到目标文件包含ELF头、程序头、节等内容, 对于.o目标文件或.so库文件,编译器在链接阶段利用这些信息把多个文件组织起来, 对于可执行文件,系统在运行时根据这些信息加载程序运行。
(4)链接阶段
最后将每个源文件对应的目标.o文件链接起来,就生成一个可执行程序文件,这是链接器ld完成的工作。
例如一个工程里包含了A和B两个代码文件,在链接阶段, 链接过程需要把A和B之间的函数调用关系理顺,也就是说要告诉A在哪里能够调用到fun函数, 建立映射关系,所以称之为链接。若链接过程中找不到fun函数的具体定义,则会链接报错。
虽然本示例只有一个hello.c文件,但它调用了C标准代码库的printf函数, 所以链接器会把它和printf函数链接起来,生成最终的可执行文件。
链接分为两种:
- 动态链接:GCC编译时的默认选项。动态是指在应用程序运行时才去加载外部的代码库,不同的程序可以共用代码库。 所以动态链接生成的程序比较小,占用较少的内存。
- 静态链接:链接时使用选项 “—static”,它在编译阶段就会把所有用到的库打包到自己的可执行程序中。 所以静态链接的优点是具有较好的兼容性,不依赖外部环境,但是生成的程序比较大。
执行如下命令体验静态链接与动态链接的区别:
1 | # 在hello.o所在的目录执行如下命令 |

从图中可以看到,使用动态链接生成的hello程序才8.3KB, 而使用静态链接生成的hello_static程序则高达845KB。
在Ubuntu下,可以使用 ldd 工具查看动态文件的库依赖,尝试执行如下命令:

可以看到,动态链接生成的hello程序依赖于库文件linux-vdso.so.1、libc.so.6 以及ld-linux-x86-64.so.2,其中的libc.so.6就是我们常说的C标准代码库, 我们的程序中调用了它的printf库函数。
静态链接生成的hello_static没有依赖外部库文件。
3.GCC编译器命名格式
除了我们安装的arm-linux-gnueabihf-gcc外,编译器还有很多版本,如arm-linux-gnueabi-gcc,本地编译器gcc全名为x86_64-linux-gnu-gcc,这些编译器是有一定的命名规则的:
1 | arch [-os] [-(gnu)eabi(hf)] -gcc |
其中的各字段如下表所示:

以我们安装的arm-linux-gnueabihf-gcc编译器为例:
- arm:表示它的目标芯片架构为ARM
- linux:目标操作系统为Linux
- gnu:使用GNU的C标准库即glibc
- eabi:使用嵌入式应用二进制接口(eabi)
- hf:编译器的浮点模式为硬浮点hard-float
4.GCC命令常用参数
1、GCC优化级别:
- gcc中指定优化级别的参数有:-O0、-O1、-O2、-O3、-Og、-Os、-Ofast。
- 在编译时,如果没有指定上面的任何优化参数,则默认为 -O0,即没有优化。
- 参数 -O1、-O2、-O3 中,随着数字变大,代码的优化程度也越高,不过这在某种意义上来说,也是以牺牲程序的可调试性为代价的。
- 参数 -Og 是在 -O1 的基础上,去掉了那些影响调试的优化,所以如果最终是为了调试程序,可以使用这个参数。不过光有这个参数也是不行的,这个参数只是告诉编译器,编译后的代码不要影响调试,但调试信息的生成还是靠 -g 参数的。
- 参数 -Os 是在 -O2 的基础上,去掉了那些会导致最终可执行程序增大的优化,如果想要更小的可执行程序,可选择这个参数。
- 参数 -Ofast 是在 -O3 的基础上,添加了一些非常规优化,这些优化是通过打破一些国际标准(比如一些数学函数的实现标准)来实现的,所以一般不推荐使用该参数。
- 如果想知道上面的优化参数具体做了哪些优化,可以使用
gcc -Q --help=optimizers命令来查询,比如下面是查询 -O3 参数开启了哪些优化:
1 | $ gcc -Q --help=optimizers -O3 |
有关gcc优化的更多详细信息,请参考gcc的官方文档:
2、常用参数:
1、gcc -E source_file.c
-E,只执行到预编译。直接输出预编译结果;
2、gcc -S source_file.c
-S,只执行到源代码到汇编代码的转换,输出汇编代码;
3、gcc -c source_file.c
-c,只执行到编译,输出目标文件;
4、gcc (-E/S/c/) source_file.c -o output_filename
-o: 指定输出文件名,可以配合以上三种标签使用;
-o 参数可以被省略。这种情况下编译器将使用以下默认名称输出:-E:预编译结果将被输出到标准输出端口(通常是显示器)
-S:生成名为source_file.s的汇编代码
-c:生成名为source_file.o的目标文件。
无标签情况:生成名为a.out的可执行文件。
5、gcc -g source_file.c
-g,生成供调试用的可执行文件,可以在gdb中运行。由于文件中包含了调试信息因此运行效率很低,且文件也大不少。
这里可以用strip命令重新将文件中debug信息删除。这是会发现生成的文件甚至比正常编译的输出更小了,这是因为strip把原先正常编译中的一些额外信息(如函数名之类)也删除了。用法为 strip a.out
6、gcc -s source_file.c
-s,直接生成与运用strip同样效果的可执行文件(删除了所有符号信息)。
7、gcc -O source_file.c
-O(大写的字母O),编译器对代码进行自动优化编译,输出效率更高的可执行文件。
-O 后面还可以跟上数字指定优化级别,如:
gcc -O2 source_file.c
数字越大,越加优化。但是通常情况下,自动的东西都不是太聪明,太大的优化级别可能会使生成的文件产生一系列的bug。一般可选择2;3会有一定风险。
8、gcc -Wall source_file.c
-W,在编译中开启一些额外的警告(warning)信息;
-Wall,将所有的警告信息全开;
9、gcc source_file.c -L/path/to/lib -lxxx -I/path/to/include
-l,指定所使用到的函数库,本例中链接器会尝试链接名为libxxx.a的函数库;
-L,指定函数库所在的文件夹,本例中链接器会尝试搜索/path/to/lib文件夹;
-I,指定头文件所在的文件夹,本例中预编译器会尝试搜索/path/to/include文件夹;
3、调试选项:
1、-g
只是编译器,在编译的时候,产生调试信息;
2、-gstabs
此选项以stabs格式声称调试信息,但是不包括gdb调试信息;
3、3-gstabs+
此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息;
4、-ggdb
此选项将尽可能的生成gdb的可以使用的调试信息;
5、-glevel
请求生成调试信息,同时用level指出需要多少信息,默认的level值是2;
4、链接选项:
1、-static 此选项将禁止使用动态库。
优点:程序运行不依赖于其他库;
缺点:文件比较大;
2、-shared (-G) 此选项将尽量使用动态库,为默认选项
优点:生成文件比较小;
缺点:运行时需要系统提供动态库;
3、-symbolic 建立共享目标文件的时候,把引用绑定到全局符号上.
对所有无法解析的引用作出警告(除非用连接编辑选项 `-Xlinker -z -Xlinker defs’取代);
注:只有部分系统支持该选项;
5、错误和警告选项:
1、-Wall
一般使用该选项,允许发出GCC能够提供的所有有用的警告。也可以用-W{warning}来标记指定的警告;
2、-pedantic
允许发出ANSI/ISO C标准所列出的所有警告;
3、-pedantic-errors
允许发出ANSI/ISO C标准所列出的错误;
4、-werror
把所有警告转换为错误,以在警告发生时中止编译过程;
5、-w
关闭所有警告,建议不要使用此项;
6、预处理选项:
1、-Dmacro
相当于C语言中的#define macro;
2、-Dmacro=defn
相当于C语言中的#define macro=defn;
3、-Umacro
相当于C语言中的#undef macro;
4、-undef
取消对任何非标准宏的定义;
7、其他选项:
1、-fpic
编译器就生成位置无关目标码.适用于共享库(shared library).
2、-fPIC
编译器就输出位置无关目标码.适用于动态连接(dynamic linking),即使分支需要大范围转移.
3、-v
显示详细的编译、汇编、连接命令