C-C++编译器-GCC
GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器。它是一套以GPL及LGPL许可证所发行的自由软件,也是GNU计划的关键部分,亦是自由的类Unix 及苹果计算机 Mac OS X 操作系统的标准编译器。GCC(特别是其中的C语言编译器)也常被认为是跨平台编译器的事实标准。
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java, 以及 Ada 与其他语言。

The GNU Compiler Collection includes front ends for C, C++, Objective-C, Fortran, Java, Ada, and Go, as well as libraries for these languages (libstdc++, libgcj,...). GCC was originally written as the compiler for the GNU operating system.
当 RMS 重返自由软件基金会(FSF,Free Software Foundation)董事会时,FSF 董事会并未考虑其他人会如何看待他的回归。鉴于这一事件引发的风波,GCC 指导委员会(GCC Steering Committee)此前已将 RMS 从 GCC 的最初创建者成员中移除,GCC 指导委员会在2021年5月宣布,他们还将放弃长期以来要求所有代码贡献的版权转让给 FSF 的政策。
长期以来,GCC 要求任何代码贡献都要向自由软件基金会转让版权,这对一些开发者或企业而言是个问题。尤其是最近随着自由软件基金会不断受到抨击,反对者中有很多人在倡议要 fork 一个 GNU Compiler Collection(GCC) 并基于此进行开发,抑或是将这个开源编译器完全从自由软件基金会中剥离出来。基于这些原因,GCC 指导委员会决定不再要求有争议的版权转让。GCC 指导委员会表示,GCC 将继续在 GPLv3 下开发,但不再需要 FSF 的版权转让。相反,贡献者可以在他们的 Git 信息中使用带有 Signed-off-by 标签的 Developer Certificate of Origin(开发者起源证书)。
公告全文如下(点击查看原文):
GCC 起初是作为 GNU 项目的一部分创建的,但如今已经发展成一个独立的项目了。
GCC 指导委员会已经决定放宽对所有经过修改的软件的版权都要转让给自由软件基金会的要求。GCC 将继续在 GNU-General Public License v3.0 下开发、分发和授权。GCC 现在将接受包含或不包含 FSF 版权转让的代码贡献。这一变化与许多其他主要的自由软件项目的做法是一致的,例如 Linux 内核。
有 FSF 版权转让的贡献者不需要改变任何东西。希望使用 Developer Certificate of Origin(开发者起源证书)的贡献者应该在他们的提交信息中添加一个 Signed-off-by 信息。有提交权限的开发者可以将他们的名字添加到 MAINTAINERS 文件中的 DCO 列表,以便为所有未来的提交提供 DCO 认证,而不是为每个提交单独提供一个 Signed-off-by 信息。GCC 指导委员会继续肯定自由软件的原则,这一点将永远不会改变。
GCC由一系列阶段组成,并由一个驱动程序将其紧密结合在一起。它们是:预处理器、解析器、代码生成器、汇编器和链接器。前三个阶段都接受可读文本格式的输入,然后输出可读的文件格式(汇编器必须输出而链接器必须输入二进制格式,这一点从定义便可理解)。运用gcc驱动程序的没命令行选项不仅可以看到C处理之后、汇编生成之后和目标码生成之后的各个结果,而且可以监控解析进程和代码生成进程中的许多中间步骤的结果。这种组织有很多好处,其中对GCC特别重要的一个好处体现在回归测试中。
GCC 有超过100个的编译选项可用. 这些选项中的许多你可能永远都不会用到, 但一些主要的选项将会频繁用到. 很多的 GCC 选项包括一个以上的字符. 因此你必须为每个选项指定各自的连字符, 并且就象大多数 Linux 命令一样不能在一个单独的连字符后跟一组选项.例如下面的两个命令是不同的:
gcc -p -g test.c
gcc -pg test.c
第一条命令告诉 GCC 编译 test.c 时为 prof 命令建立剖析(profile)信息并且把调试信息加入到可执行的文件里. 第二条命令只告诉 GCC 为 gprof 命令建立剖析信息.当不用任何选项编译一个程序时, GCC 将会建立(假定编译成功)一个名为 a.out 的可执行文件,能用 -o 编译选项来为将产生的可执行文件指定一个文件名来代替 a.out. 例如,将一个叫 count.c 的 C 程序编译为名叫 count 的可执行文件, 可输入下面的命令:
gcc -o count count.c
注意当使用 -o 选项时, -o 后面必须跟一个文件名。
GCC 同样有指定编译器处理多少的编译选项. -c 选项告诉 GCC 仅把源代码编译为目标代码而跳过汇编和连接的步骤. 这个选项使用的非常频繁因为它使得编译多个 C 程序时速度更快并且更易于管理.缺省时 GCC 建立的目标代码文件有一个 .o 的扩展名.
-S 编译选项告诉 GCC 在为 C 代码产生了汇编语言文件后停止编译. GCC 产生的汇编语言文件的缺省扩展名是 .s . -E 选项指示编译器仅对输入文件进行预处理. 当这个选项被使用时, 预处理器的输出被送到标准输出而不是储存在文件里.
优化选项
当用 GCC 编译 C 代码时, 它会试着用最少的时间完成编译并且使编译后的代码易于调试. 易于调试意味着编译后的代码与源代码有同样的执行次序, 编译后的代码没有经过优化. 有很多选项可用于告诉 GCC 在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的可执行文件. 这些选项中最典型的是-O 和 -O2 选项.
-O 选项告诉 GCC 对源代码进行基本优化. 这些优化在大多数情况下都会使程序执行的更快. -O2 选项告诉 GCC 产生尽可能小和尽可能快的代码. -O2 选项将使编译的速度比使用 -O 时慢. 但通常产生的代码执行速度会更快.除了 -O 和 -O2 优化选项外, 还有一些低级选项用于产生更快的代码. 这些选项非常的特殊, 而且最好只有当你完全理解这些选项将会对编译后的代码产生什么样的效果时再去使用. 这些选项的详细描述, 请参考 GCC 的指南页, 在命令行上键入 man gcc .
调试和剖析选项
GCC 支持数种调试和剖析选项,在这些选项里你会最常用到的是 -g 和 -pg 选项.
-g 选项告诉 GCC 产生能被 GNU 调试器使用的调试信息以便调试你的程序. GCC 提供了一个很多其他 C 编译器里没有的特性, 在 GCC 里你能使 -g 和 -O (产生优化代码)联用. 这一点非常有用因为你能在与最终产品尽可能相近的情况下调试你的代码. 在你同时使用这两个选项时你必须清楚你所写的某些代码已经在优化时被 GCC 作了改动.
-pg 选项告诉 GCC 在你的程序里加入额外的代码, 执行时, 产生 gprof 用的剖析信息以显示你的程序的耗时情况.
另外提供了一个叫 gdb 的 GNU 调试程序用来调试 C 和 C++ 程序的强力调试器. 它能在程序运行时观察程序的内部结构和内存的使用情况,以下是 gdb 所提供的一些功能:
它使你能监视你程序中变量的值.
它使你能设置断点以使程序在指定的代码行上停止执行.
它使你能一行行的执行代码.
为了使 gdb 正常工作, 必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联.在编译时用 -g 选项打开调试选项。它支持很多的命令来实现不同的功能.这些命令从简单的文件装入到允许检查所调用的堆栈内容的复杂命令,下面列出了在用 gdb 调试时会用到的一些命令.
命令 描 述
file 装入想要调试的可执行文件.
kill 终止正在调试的程序.
list 列出产生执行文件的源代码的一部分.
next 执行一行源代码但不进入函数内部.
step 执行一行源代码而且进入函数内部.
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变.
break 在代码里设置断点, 这将使程序执行到这里时被挂起.
make 使你能不退出 gdb 就可以重新产生可执行文件.
shell 使你能不离开 gdb 就执行 UNIX shell 命令.
gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让 gdb 帮你补齐一个唯一的命令, 如果不唯一的话 gdb 会列出所有匹配的命令. 也能用光标键上下翻动历史命令.
xxgdb 是 gdb 的一个基于 X Window 系统的图形界面. xxgdb 包括了命令行版的 gdb 上的所有特性. xxgdb 使你能通过按按钮来执行常用的命令. 设置了断点的地方也用图形来显示.
cproto 读入 C 源程序文件并自动为每个函数产生原型申明,用它可以在写程序时为你节省大量用来定义函数原型的时间.
indent 实用程序是 Linux 里包含的另一个编程实用工具. 这个工具简单的说就为你的代码产生美观的缩进的格式. indent 也有很多选项来指定如何格式化你的源代码.indent 并不改变代码的实质内容, 而只是改变代码的外观. 使它变得更可读,这永远是一件好事。
gprof 是安装在你的 Linux 系统的 /usr/bin 目录下的一个程序. 它使你能剖析你的程序从而知道程序的哪一个部分在执行时最费时间.将告诉程序里每个函数被调用的次数和每个函数执行时所占时间的百分比.如果想提高程序性能的话这些信息非常有用.
为了在程序上使用 gprof, 必须在编译程序时加上 -pg 选项. 这将使程序在每次执行时产生一个叫 gmon.out 的文件. gprof 用这个文件产生剖析信息.在运行了你的程序并产生了 gmon.out 文件后你能用下面的命令获得剖析信息:
gprof <program_name>
参数 program_name 是产生 gmon.out 文件的程序的名字.
技巧: gprof 产生的剖析数据很大, 如果想检查这些数据的话最好把输出重定向到一个文件里.
f2c 和 p2c 是两个源代码转换程序. f2c 把 FORTRAN 代码转换为 C 代码, p2c 把 Pascal 代码转换为 C 代码. 当你安装 GCC 时这两个程序都会被安装上去.
如果有一些用 FORTRAN 或 Pascal 写的代码要用 C 重写的话, f2c 和 p2c 对你非常有用. 这两个程序产生的 C 代码一般不用修改就直接能被 GCC 编译.如果要转换的 FORTRAN 或 Pascal 程序比较小的话可以直接使用 f2c 或 p2c 不用加任何选项. 如果要转换的程序比较庞大, 包含很多文件的话你可能要用到一些命令行选项.
GCC编译器使用简述
一、关于GCC
GCC的全称是GNU Compiler Collection,是GNU工具链中的一种。GCC不仅支持C/C++语言,还支持Fortran/Ada/Java等语言的编译。
GCC和gcc是两个概念,GCC是工具链的集合,里面除了gcc/g++还包含了ccl,cclplus等组件。gcc/g++只是GCC工具链的一个子集。
二、g++和gcc的区别
gcc可以判断出目标程序所使用编程语言的类别,会把xxx.c文件当作C语言编译,把xxx.cpp文件当作C++语言编译。而g++只把xxx.c和xxx.cpp一律都当作C++语言来编译。
在编译C++文件的时候,g++会自动链接一些标准库或基础库,而gcc不会。当正在编译的C++代码文件依赖STL标准库的时候,为了使用STL,gcc命令需要增加参数–lstdc++。因此,虽然gcc和g++都可以编译C++语言程序,但是使用g++会更方便一些。
三、常见代码文件后缀名
1)目标文件:
xxx.o, 操作系统:Linux, Mac
xxx.obj, 操作系统:windows
2)二进制文件:
xxx(没有后缀名), 操作系统:Linux, Mac, FreeBSD,
xxx.exe, 操作系统:windows
xxx.hex,操作系统:嵌入式系统
3)共享库文件,也叫动态库文件:
xxx.dll, 操作系统:windows
xxx.so, 操作系统:Linux
xxx.dylib, 操作系统:Mac
4)静态库文件
xxx.a
四、C/C++语言的编译过程
1.预处理
预处理命令声明了编译时需要的各种头文件和宏,比如包含哪些头文件、宏定义的扩展、在哪个代码段做条件编译等。涉及预处理的语法有:#define,#include,#ifdef...#endif等。
2.编译
首先检查代码的规范性和语法错误等,检查完毕后把代码翻译成汇编语言,生成汇编语言文件
3.汇编
基于汇编语言文件生成二进制格式的目标文件
3.链接
将目标代码与所依赖的库文件进行关联或者组装,合成一个可执行文件
具体过程如图:

用g++举例
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
g++的编译过程:
1.预处理--将xx.cpp源文件预处理成xx.i文件
g++ -E demo.cpp -o demo.i
2.编译--将xx.i文件编译为xx.s的汇编文件。此时只进行编译生成汇编代码,而不对代码以汇编的方式调试
g++ -S demo.i -o demo.s
3.汇编--将xx.s文件汇编成xx.o的二进制目标文件
g++ -c demo.s -o demo.o
4.链接--将xx.o二进制文件进行链接,最终生成可执行程序
g++ demo.o -o demo.out
五、静态链接和动态链接的区别
静态库:
与目标程序合并,成为目标程序的一部分。
创建静态库的时候,需要使用"gcc/g++ -c"先将xxx.c源文件编译为目标文件xxx.o,然后使用ar指令将xxx.o打包成xxxx.a静态库。
目标程序与静态库链接时,目标程序代码调用的任何外部函数的代码都会从静态库中复制到最终的可执行文件中。
GCC在链接时优先使用动态库,只有当动态库不存在时才开始使用静态库,如果要强制使用静态库,编译时加上-static参数。
使用-Wl,-Bstatic告诉链接器优先使用静态库。
动态库:
不包含在目标程序中,但是与目标程序相关联。
创建动态库的时候,可以传-shared和-fPIC参数,-fPIC参数用于编译阶段,用来生成位置无关的代码。使用“gcc -shared -fPIC”可以直接用xxx.c源文件生成xxx.so动态库。
目标程序与动态库链接时,可执行文件仅包含它所需的一个小函数表,而不是来自库文件的完整机器代码。在可执行文件开始运行之前,动态库的代码被操作系统复制到内存中进行共享。
动态库之所以叫共享库,可能是由于动态库的代码副本可以在多个程序之间共享。正因为这种链接方式,共享库每次被更新时,都不需要重新编译正在使用共享库的目标程序。
使用-Wl,-Bdynamic告诉链接器优先使用动态库。
g++ -shared -fPIC demo.cpp -o libdemo.so
有关的环境变量:
LIBRARY_PATH:使用于编译期间,目标程序链接时搜索动态库的路径。
LD_LIBRARY_PATH:使用于目标程序生成后,目标程序运行时搜索动态库的路径。
静态库链接时,搜索库文件路径的顺序:
1. ld会去找GCC命令中的参数-L
2. gcc的环境变量LIBRARY_PATH
3. /lib,/usr/lib,/usr/local/lib等写在程序内的路径
动态库链接时,搜索库文件路径的顺序:
1. 编译目标代码时指定的动态库搜索路径
2. gcc的环境变量LD_LIBRARY_PATH
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
实用程序:ldd 和 nm
ldd:列出依赖的动态库
# ldd freeoa
nm:查看动态库/静态库中的函数
# nm *.so | grep main
六、gcc/g++命令常见参数
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-pedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
常见参数如下(注意大小写):
-o
#输出到指定文件。如果不指定,默认输出到a.out
-E
#仅进行预处理,不进行编译、汇编和链接
-S
#将代码转换为文件格式为xxx.s的汇编语言文件,但不进行汇编
-c
#仅进行编译和汇编,不进行链接操作,常用于编译不包含main程序的子程序代码
-v
#打印gcc编译时的详细步骤信息
编译和路径参数
-l[basic library]
#编译时指定要使用的基础库,样例:-lpthread,针对Posix线程共享库进行编译
-L[shared-library path]
#共享库的路径添加到搜索的范围,路径为包含xxx.dll/xxx.so/xxx.dlyb文件的目录
-I[include header-file path]
#将头文件的路径添加到搜索的范围,路径为包含xxx.h/xxx.hpp文件的目录
-shared
#生成共享库,库文件格式为xxx.dll/xxx.so/xxx.dlyb格式的文件
-static
#生成静态库,库文件格式为xxx.a格式的文件
-Wl
#告诉编译器将后面的参数传递给链接器
-Wl,-Bstatic
#-Bstatic选项用于对指定的库静态连接
-Wl,-Bdynamic
#-Bdynamic搜索共享库(默认)
-Wa,option
#此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序
-Wl,option
#此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序
预处理参数
#使用形式:-D[FLAG] 或-D[FLAG]=VALUE
-Dmacro
#在命令行里定义宏,相当于C语言中的"#define macro"
-Umacro
#相当于C语言中的"#undef macro"
-undef
#取消对任何非标准宏的定义
警告与报错参数
-Wall
#发出gcc提供的所有有用的报警信息
-Werror
#将警告升级为编译报错
-Wextra / -W
#启用-Wall未启用的额外警告位,对合法但值得怀疑的代码发出警告 例如 -Wsign-compare
-pendantic / -Wpendantic
#发出ISO C和ISO C++标准列出的所有警告,用于语法检查,-pedantic-erros的用法也类似
-fsyntax-only
#仅做语法检查
调试参数
-g
#产生带有调试信息的目标代码
-gstabs
#此选项以stabs格式声称调试信息,但是不包括gdb调试信息
-gstabs+
#此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息
-ggdb
#生成gdb专用的调试信息
-glevel
#请求生成调试信息,同时用level指出需要多少信息,默认的level值是2
编码配置参数
-fno-exceptions
#屏蔽掉C++的异常,常用于于嵌入式或无法接受异常的系统
-fno-rtti
#禁用RTTI,常用于嵌入式或游戏开发
-fno-asm
#不要识别asm,inline或typeof作为关键字,以便代码可以使用这些词作为标识符。您可以使用关键字__asm__,__inline__来__typeof__ 代替。-ansi暗示-fno-asm
-fPIC / -fpic
#让编译器的代码和位置无关,让代码逻辑不使用绝对地址,只用相对地址,方便文件加载
-nostdinc
#使编译器不再系统默认的头文件目录里面找头文件, 一般和 -I 联合使用,明确限定头文件的位置
-nostdin C++
#规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建libg++库使用
优化参数
-O0
#不优化
-O1 / -O
#尝试优化编译时间和可执行文件大小
-O2
#尝试所有的优化选项,但不会进行“空间换时间”的优化方式
-Os
#尝试所有的优化选项时,优先优化可执行文件大小
七、参考阅读
https://gcc.gnu.org/onlinedocs/gcc/
https://subscription.packtpub.com/book/programming/
GCC Rust 得到批准将被纳入主线代码库
GCC Front-End For Rust(也称为 GCC Rust)是 Rust 语言在 GCC 之上的一个完整替代性实现,目标是成为 GNU 工具链的完全上游。由于这是一个前端项目,该编译器将获得对所有 GCC 内部中端优化通道的完全访问权,这与 LLVM 不同。这个编译器的用户可以使用熟悉的 -O2 标志来调整 GCC 的优化器。与 GCC 的紧密结合将对一些项目很有帮助,这些项目也将能够从 GCC 插件中受益。当然 GCC 也会带来对更多目标架构的支持,基于 GCC 的 Rust 编译器将使 GCC Rust 在新的平台上更加容易启动。

在该项目创立时,Rust 还处于 0.9 版本,随着 Rust 语言已经足够稳定,这是创建替代性编译器的绝佳时机。从 2020 年 11 月开始,开发者 Philip Herron 就已全职从事 GCC Rust 的开发工作,在他和整个社区的共同努力下,GCC 指导委员会于2022年7月中旬正式宣布接受 GCC Rust 对 GCC 的贡献,GCC Rust 将被纳入其主线代码库,由 GCC 提供 Rust 编程语言支持。这个 Rust 前端可能会在2023年的 GCC 13 发布之前被合并,而 GCC 13 将在2023年4月左右作为稳定版发布。
该项目仍处于早期阶段,目标是率先实现编译官方的 Rust 测试套件,目前也暂时不会支持 proc_macro crate 和 Rust 借用检查器这样的功能。开发者希望 GCC Rust 在 GCC 13 中对 Rust 编程语言至少有 "测试" 级别的支持。Rust 的设计准则为“安全、并发、实用”,在确保性能和原生编译语言一样的同时,能够实现内存安全。这样的特性也促使如今有越来越多的公司开始使用 Rust 编程语言,支持 Rust 项目。Rust for Linux 也有望在 Linux 5.20 中实现。
GCC版本更新录(202x)
项目主页:http://gcc.gnu.org/
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java, 以及 Ada 与其他语言。

The GNU Compiler Collection includes front ends for C, C++, Objective-C, Fortran, Java, Ada, and Go, as well as libraries for these languages (libstdc++, libgcj,...). GCC was originally written as the compiler for the GNU operating system.
当 RMS 重返自由软件基金会(FSF,Free Software Foundation)董事会时,FSF 董事会并未考虑其他人会如何看待他的回归。鉴于这一事件引发的风波,GCC 指导委员会(GCC Steering Committee)此前已将 RMS 从 GCC 的最初创建者成员中移除,GCC 指导委员会在2021年5月宣布,他们还将放弃长期以来要求所有代码贡献的版权转让给 FSF 的政策。
长期以来,GCC 要求任何代码贡献都要向自由软件基金会转让版权,这对一些开发者或企业而言是个问题。尤其是最近随着自由软件基金会不断受到抨击,反对者中有很多人在倡议要 fork 一个 GNU Compiler Collection(GCC) 并基于此进行开发,抑或是将这个开源编译器完全从自由软件基金会中剥离出来。基于这些原因,GCC 指导委员会决定不再要求有争议的版权转让。GCC 指导委员会表示,GCC 将继续在 GPLv3 下开发,但不再需要 FSF 的版权转让。相反,贡献者可以在他们的 Git 信息中使用带有 Signed-off-by 标签的 Developer Certificate of Origin(开发者起源证书)。
公告全文如下(点击查看原文):
GCC 起初是作为 GNU 项目的一部分创建的,但如今已经发展成一个独立的项目了。
GCC 指导委员会已经决定放宽对所有经过修改的软件的版权都要转让给自由软件基金会的要求。GCC 将继续在 GNU-General Public License v3.0 下开发、分发和授权。GCC 现在将接受包含或不包含 FSF 版权转让的代码贡献。这一变化与许多其他主要的自由软件项目的做法是一致的,例如 Linux 内核。
有 FSF 版权转让的贡献者不需要改变任何东西。希望使用 Developer Certificate of Origin(开发者起源证书)的贡献者应该在他们的提交信息中添加一个 Signed-off-by 信息。有提交权限的开发者可以将他们的名字添加到 MAINTAINERS 文件中的 DCO 列表,以便为所有未来的提交提供 DCO 认证,而不是为每个提交单独提供一个 Signed-off-by 信息。GCC 指导委员会继续肯定自由软件的原则,这一点将永远不会改变。
GCC由一系列阶段组成,并由一个驱动程序将其紧密结合在一起。它们是:预处理器、解析器、代码生成器、汇编器和链接器。前三个阶段都接受可读文本格式的输入,然后输出可读的文件格式(汇编器必须输出而链接器必须输入二进制格式,这一点从定义便可理解)。运用gcc驱动程序的没命令行选项不仅可以看到C处理之后、汇编生成之后和目标码生成之后的各个结果,而且可以监控解析进程和代码生成进程中的许多中间步骤的结果。这种组织有很多好处,其中对GCC特别重要的一个好处体现在回归测试中。
GCC 有超过100个的编译选项可用. 这些选项中的许多你可能永远都不会用到, 但一些主要的选项将会频繁用到. 很多的 GCC 选项包括一个以上的字符. 因此你必须为每个选项指定各自的连字符, 并且就象大多数 Linux 命令一样不能在一个单独的连字符后跟一组选项.例如下面的两个命令是不同的:
gcc -p -g test.c
gcc -pg test.c
第一条命令告诉 GCC 编译 test.c 时为 prof 命令建立剖析(profile)信息并且把调试信息加入到可执行的文件里. 第二条命令只告诉 GCC 为 gprof 命令建立剖析信息.当不用任何选项编译一个程序时, GCC 将会建立(假定编译成功)一个名为 a.out 的可执行文件,能用 -o 编译选项来为将产生的可执行文件指定一个文件名来代替 a.out. 例如,将一个叫 count.c 的 C 程序编译为名叫 count 的可执行文件, 可输入下面的命令:
gcc -o count count.c
注意当使用 -o 选项时, -o 后面必须跟一个文件名。
GCC 同样有指定编译器处理多少的编译选项. -c 选项告诉 GCC 仅把源代码编译为目标代码而跳过汇编和连接的步骤. 这个选项使用的非常频繁因为它使得编译多个 C 程序时速度更快并且更易于管理.缺省时 GCC 建立的目标代码文件有一个 .o 的扩展名.
-S 编译选项告诉 GCC 在为 C 代码产生了汇编语言文件后停止编译. GCC 产生的汇编语言文件的缺省扩展名是 .s . -E 选项指示编译器仅对输入文件进行预处理. 当这个选项被使用时, 预处理器的输出被送到标准输出而不是储存在文件里.
优化选项
当用 GCC 编译 C 代码时, 它会试着用最少的时间完成编译并且使编译后的代码易于调试. 易于调试意味着编译后的代码与源代码有同样的执行次序, 编译后的代码没有经过优化. 有很多选项可用于告诉 GCC 在耗费更多编译时间和牺牲易调试性的基础上产生更小更快的可执行文件. 这些选项中最典型的是-O 和 -O2 选项.
-O 选项告诉 GCC 对源代码进行基本优化. 这些优化在大多数情况下都会使程序执行的更快. -O2 选项告诉 GCC 产生尽可能小和尽可能快的代码. -O2 选项将使编译的速度比使用 -O 时慢. 但通常产生的代码执行速度会更快.除了 -O 和 -O2 优化选项外, 还有一些低级选项用于产生更快的代码. 这些选项非常的特殊, 而且最好只有当你完全理解这些选项将会对编译后的代码产生什么样的效果时再去使用. 这些选项的详细描述, 请参考 GCC 的指南页, 在命令行上键入 man gcc .
调试和剖析选项
GCC 支持数种调试和剖析选项,在这些选项里你会最常用到的是 -g 和 -pg 选项.
-g 选项告诉 GCC 产生能被 GNU 调试器使用的调试信息以便调试你的程序. GCC 提供了一个很多其他 C 编译器里没有的特性, 在 GCC 里你能使 -g 和 -O (产生优化代码)联用. 这一点非常有用因为你能在与最终产品尽可能相近的情况下调试你的代码. 在你同时使用这两个选项时你必须清楚你所写的某些代码已经在优化时被 GCC 作了改动.
-pg 选项告诉 GCC 在你的程序里加入额外的代码, 执行时, 产生 gprof 用的剖析信息以显示你的程序的耗时情况.
另外提供了一个叫 gdb 的 GNU 调试程序用来调试 C 和 C++ 程序的强力调试器. 它能在程序运行时观察程序的内部结构和内存的使用情况,以下是 gdb 所提供的一些功能:
它使你能监视你程序中变量的值.
它使你能设置断点以使程序在指定的代码行上停止执行.
它使你能一行行的执行代码.
为了使 gdb 正常工作, 必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号. gdb 利用这些信息使源代码和机器码相关联.在编译时用 -g 选项打开调试选项。它支持很多的命令来实现不同的功能.这些命令从简单的文件装入到允许检查所调用的堆栈内容的复杂命令,下面列出了在用 gdb 调试时会用到的一些命令.
命令 描 述
file 装入想要调试的可执行文件.
kill 终止正在调试的程序.
list 列出产生执行文件的源代码的一部分.
next 执行一行源代码但不进入函数内部.
step 执行一行源代码而且进入函数内部.
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变.
break 在代码里设置断点, 这将使程序执行到这里时被挂起.
make 使你能不退出 gdb 就可以重新产生可执行文件.
shell 使你能不离开 gdb 就执行 UNIX shell 命令.
gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让 gdb 帮你补齐一个唯一的命令, 如果不唯一的话 gdb 会列出所有匹配的命令. 也能用光标键上下翻动历史命令.
xxgdb 是 gdb 的一个基于 X Window 系统的图形界面. xxgdb 包括了命令行版的 gdb 上的所有特性. xxgdb 使你能通过按按钮来执行常用的命令. 设置了断点的地方也用图形来显示.
cproto 读入 C 源程序文件并自动为每个函数产生原型申明,用它可以在写程序时为你节省大量用来定义函数原型的时间.
indent 实用程序是 Linux 里包含的另一个编程实用工具. 这个工具简单的说就为你的代码产生美观的缩进的格式. indent 也有很多选项来指定如何格式化你的源代码.indent 并不改变代码的实质内容, 而只是改变代码的外观. 使它变得更可读,这永远是一件好事。
gprof 是安装在你的 Linux 系统的 /usr/bin 目录下的一个程序. 它使你能剖析你的程序从而知道程序的哪一个部分在执行时最费时间.将告诉程序里每个函数被调用的次数和每个函数执行时所占时间的百分比.如果想提高程序性能的话这些信息非常有用.
为了在程序上使用 gprof, 必须在编译程序时加上 -pg 选项. 这将使程序在每次执行时产生一个叫 gmon.out 的文件. gprof 用这个文件产生剖析信息.在运行了你的程序并产生了 gmon.out 文件后你能用下面的命令获得剖析信息:
gprof <program_name>
参数 program_name 是产生 gmon.out 文件的程序的名字.
技巧: gprof 产生的剖析数据很大, 如果想检查这些数据的话最好把输出重定向到一个文件里.
f2c 和 p2c 是两个源代码转换程序. f2c 把 FORTRAN 代码转换为 C 代码, p2c 把 Pascal 代码转换为 C 代码. 当你安装 GCC 时这两个程序都会被安装上去.
如果有一些用 FORTRAN 或 Pascal 写的代码要用 C 重写的话, f2c 和 p2c 对你非常有用. 这两个程序产生的 C 代码一般不用修改就直接能被 GCC 编译.如果要转换的 FORTRAN 或 Pascal 程序比较小的话可以直接使用 f2c 或 p2c 不用加任何选项. 如果要转换的程序比较庞大, 包含很多文件的话你可能要用到一些命令行选项.
GCC编译器使用简述
一、关于GCC
GCC的全称是GNU Compiler Collection,是GNU工具链中的一种。GCC不仅支持C/C++语言,还支持Fortran/Ada/Java等语言的编译。
GCC和gcc是两个概念,GCC是工具链的集合,里面除了gcc/g++还包含了ccl,cclplus等组件。gcc/g++只是GCC工具链的一个子集。
二、g++和gcc的区别
gcc可以判断出目标程序所使用编程语言的类别,会把xxx.c文件当作C语言编译,把xxx.cpp文件当作C++语言编译。而g++只把xxx.c和xxx.cpp一律都当作C++语言来编译。
在编译C++文件的时候,g++会自动链接一些标准库或基础库,而gcc不会。当正在编译的C++代码文件依赖STL标准库的时候,为了使用STL,gcc命令需要增加参数–lstdc++。因此,虽然gcc和g++都可以编译C++语言程序,但是使用g++会更方便一些。
三、常见代码文件后缀名
1)目标文件:
xxx.o, 操作系统:Linux, Mac
xxx.obj, 操作系统:windows
2)二进制文件:
xxx(没有后缀名), 操作系统:Linux, Mac, FreeBSD,
xxx.exe, 操作系统:windows
xxx.hex,操作系统:嵌入式系统
3)共享库文件,也叫动态库文件:
xxx.dll, 操作系统:windows
xxx.so, 操作系统:Linux
xxx.dylib, 操作系统:Mac
4)静态库文件
xxx.a
四、C/C++语言的编译过程
1.预处理
预处理命令声明了编译时需要的各种头文件和宏,比如包含哪些头文件、宏定义的扩展、在哪个代码段做条件编译等。涉及预处理的语法有:#define,#include,#ifdef...#endif等。
2.编译
首先检查代码的规范性和语法错误等,检查完毕后把代码翻译成汇编语言,生成汇编语言文件
3.汇编
基于汇编语言文件生成二进制格式的目标文件
3.链接
将目标代码与所依赖的库文件进行关联或者组装,合成一个可执行文件
具体过程如图:

用g++举例
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
g++的编译过程:
1.预处理--将xx.cpp源文件预处理成xx.i文件
g++ -E demo.cpp -o demo.i
2.编译--将xx.i文件编译为xx.s的汇编文件。此时只进行编译生成汇编代码,而不对代码以汇编的方式调试
g++ -S demo.i -o demo.s
3.汇编--将xx.s文件汇编成xx.o的二进制目标文件
g++ -c demo.s -o demo.o
4.链接--将xx.o二进制文件进行链接,最终生成可执行程序
g++ demo.o -o demo.out
五、静态链接和动态链接的区别
静态库:
与目标程序合并,成为目标程序的一部分。
创建静态库的时候,需要使用"gcc/g++ -c"先将xxx.c源文件编译为目标文件xxx.o,然后使用ar指令将xxx.o打包成xxxx.a静态库。
目标程序与静态库链接时,目标程序代码调用的任何外部函数的代码都会从静态库中复制到最终的可执行文件中。
GCC在链接时优先使用动态库,只有当动态库不存在时才开始使用静态库,如果要强制使用静态库,编译时加上-static参数。
使用-Wl,-Bstatic告诉链接器优先使用静态库。
动态库:
不包含在目标程序中,但是与目标程序相关联。
创建动态库的时候,可以传-shared和-fPIC参数,-fPIC参数用于编译阶段,用来生成位置无关的代码。使用“gcc -shared -fPIC”可以直接用xxx.c源文件生成xxx.so动态库。
目标程序与动态库链接时,可执行文件仅包含它所需的一个小函数表,而不是来自库文件的完整机器代码。在可执行文件开始运行之前,动态库的代码被操作系统复制到内存中进行共享。
动态库之所以叫共享库,可能是由于动态库的代码副本可以在多个程序之间共享。正因为这种链接方式,共享库每次被更新时,都不需要重新编译正在使用共享库的目标程序。
使用-Wl,-Bdynamic告诉链接器优先使用动态库。
g++ -shared -fPIC demo.cpp -o libdemo.so
有关的环境变量:
LIBRARY_PATH:使用于编译期间,目标程序链接时搜索动态库的路径。
LD_LIBRARY_PATH:使用于目标程序生成后,目标程序运行时搜索动态库的路径。
静态库链接时,搜索库文件路径的顺序:
1. ld会去找GCC命令中的参数-L
2. gcc的环境变量LIBRARY_PATH
3. /lib,/usr/lib,/usr/local/lib等写在程序内的路径
动态库链接时,搜索库文件路径的顺序:
1. 编译目标代码时指定的动态库搜索路径
2. gcc的环境变量LD_LIBRARY_PATH
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
实用程序:ldd 和 nm
ldd:列出依赖的动态库
# ldd freeoa
nm:查看动态库/静态库中的函数
# nm *.so | grep main
六、gcc/g++命令常见参数
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-pedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
常见参数如下(注意大小写):
-o
#输出到指定文件。如果不指定,默认输出到a.out
-E
#仅进行预处理,不进行编译、汇编和链接
-S
#将代码转换为文件格式为xxx.s的汇编语言文件,但不进行汇编
-c
#仅进行编译和汇编,不进行链接操作,常用于编译不包含main程序的子程序代码
-v
#打印gcc编译时的详细步骤信息
编译和路径参数
-l[basic library]
#编译时指定要使用的基础库,样例:-lpthread,针对Posix线程共享库进行编译
-L[shared-library path]
#共享库的路径添加到搜索的范围,路径为包含xxx.dll/xxx.so/xxx.dlyb文件的目录
-I[include header-file path]
#将头文件的路径添加到搜索的范围,路径为包含xxx.h/xxx.hpp文件的目录
-shared
#生成共享库,库文件格式为xxx.dll/xxx.so/xxx.dlyb格式的文件
-static
#生成静态库,库文件格式为xxx.a格式的文件
-Wl
#告诉编译器将后面的参数传递给链接器
-Wl,-Bstatic
#-Bstatic选项用于对指定的库静态连接
-Wl,-Bdynamic
#-Bdynamic搜索共享库(默认)
-Wa,option
#此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序
-Wl,option
#此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序
预处理参数
#使用形式:-D[FLAG] 或-D[FLAG]=VALUE
-Dmacro
#在命令行里定义宏,相当于C语言中的"#define macro"
-Umacro
#相当于C语言中的"#undef macro"
-undef
#取消对任何非标准宏的定义
警告与报错参数
-Wall
#发出gcc提供的所有有用的报警信息
-Werror
#将警告升级为编译报错
-Wextra / -W
#启用-Wall未启用的额外警告位,对合法但值得怀疑的代码发出警告 例如 -Wsign-compare
-pendantic / -Wpendantic
#发出ISO C和ISO C++标准列出的所有警告,用于语法检查,-pedantic-erros的用法也类似
-fsyntax-only
#仅做语法检查
调试参数
-g
#产生带有调试信息的目标代码
-gstabs
#此选项以stabs格式声称调试信息,但是不包括gdb调试信息
-gstabs+
#此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息
-ggdb
#生成gdb专用的调试信息
-glevel
#请求生成调试信息,同时用level指出需要多少信息,默认的level值是2
编码配置参数
-fno-exceptions
#屏蔽掉C++的异常,常用于于嵌入式或无法接受异常的系统
-fno-rtti
#禁用RTTI,常用于嵌入式或游戏开发
-fno-asm
#不要识别asm,inline或typeof作为关键字,以便代码可以使用这些词作为标识符。您可以使用关键字__asm__,__inline__来__typeof__ 代替。-ansi暗示-fno-asm
-fPIC / -fpic
#让编译器的代码和位置无关,让代码逻辑不使用绝对地址,只用相对地址,方便文件加载
-nostdinc
#使编译器不再系统默认的头文件目录里面找头文件, 一般和 -I 联合使用,明确限定头文件的位置
-nostdin C++
#规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创建libg++库使用
优化参数
-O0
#不优化
-O1 / -O
#尝试优化编译时间和可执行文件大小
-O2
#尝试所有的优化选项,但不会进行“空间换时间”的优化方式
-Os
#尝试所有的优化选项时,优先优化可执行文件大小
七、参考阅读
https://gcc.gnu.org/onlinedocs/gcc/
https://subscription.packtpub.com/book/programming/
GCC Rust 得到批准将被纳入主线代码库
GCC Front-End For Rust(也称为 GCC Rust)是 Rust 语言在 GCC 之上的一个完整替代性实现,目标是成为 GNU 工具链的完全上游。由于这是一个前端项目,该编译器将获得对所有 GCC 内部中端优化通道的完全访问权,这与 LLVM 不同。这个编译器的用户可以使用熟悉的 -O2 标志来调整 GCC 的优化器。与 GCC 的紧密结合将对一些项目很有帮助,这些项目也将能够从 GCC 插件中受益。当然 GCC 也会带来对更多目标架构的支持,基于 GCC 的 Rust 编译器将使 GCC Rust 在新的平台上更加容易启动。

在该项目创立时,Rust 还处于 0.9 版本,随着 Rust 语言已经足够稳定,这是创建替代性编译器的绝佳时机。从 2020 年 11 月开始,开发者 Philip Herron 就已全职从事 GCC Rust 的开发工作,在他和整个社区的共同努力下,GCC 指导委员会于2022年7月中旬正式宣布接受 GCC Rust 对 GCC 的贡献,GCC Rust 将被纳入其主线代码库,由 GCC 提供 Rust 编程语言支持。这个 Rust 前端可能会在2023年的 GCC 13 发布之前被合并,而 GCC 13 将在2023年4月左右作为稳定版发布。
该项目仍处于早期阶段,目标是率先实现编译官方的 Rust 测试套件,目前也暂时不会支持 proc_macro crate 和 Rust 借用检查器这样的功能。开发者希望 GCC Rust 在 GCC 13 中对 Rust 编程语言至少有 "测试" 级别的支持。Rust 的设计准则为“安全、并发、实用”,在确保性能和原生编译语言一样的同时,能够实现内存安全。这样的特性也促使如今有越来越多的公司开始使用 Rust 编程语言,支持 Rust 项目。Rust for Linux 也有望在 Linux 5.20 中实现。
GCC版本更新录(202x)
项目主页:http://gcc.gnu.org/
该文章最后由 阿炯 于 2025-11-11 13:45:34 更新,目前是第 3 版。