前言

介绍

GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。

GCC 原名为 GNU C语言编译器,因为它原本只能处理 C 语言,但如今的 GCC ,不仅可以编译 C、C++ 和 Objective-C,还可以通过不同的前端模块支持各种语言,包括 Java、Fortran、Ada、Pascal、Go 和 D 语言等等。

GCC支持多种硬件开发平台,还能进行跨平台交叉编译。此外,GCC是按模块化设计的,可以加入新语言和新CPU架构的支持。

GCC、gcc、g++三者之间的关系

gcc(GUN C Compiler)是GCC中的c编译器,而g++(GUN C++ Compiler)是GCC中的c++编译器。

gcc和g++两者都可以编译c和cpp文件,但存在差异。gcc在编译cpp时语法按照c来编译但默认不能链接到c++的库(gcc默认链接c库,g++默认链接c++库)。g++编译.c和.cpp文件都统一按cpp的语法规则来编译。所以一般编译c用gcc,编译c++用g++。

安装

linux

只需要一行指令

1
2
sudo apt install gcc
sudo apt install g++

windows

可以使用 mingw 来安装 gnuc 的工具

mingw

下载压缩包之后,解压到软件的位置,然后再添加系统环境变量

gcc编译指令参数 gcc [option] file

  1. -o 用于指定编译之后的文件名称
  2. -g 在可执行文件中加入源码信息供gdb调试使用,但是并不是把整个源文件嵌入到可执行文件中,而是几条机器码对应几行源代码
  3. -c 只生成目标文件 .o 文件
  4. -Wall 通常gcc不会提示一些不重要的警告,加上之后会提示所有警告
  5. -O 编译优化等级
    gcc 编译有 -O0, -O1, -O2, -O3, -Os, -Ofast, -Og 等几个优化等级,对于前几个来说,随着数字变大,代码的优化程度也越高,但是某种程度上来说是牺牲可调试性能作为代价的,当然有些代码部分是不能进行优化的

    • -O0 默认优化等级,实质上没有任何优化
    • -O1 最基本优化,短时间内生成可执行文件,主要对代码的分支,常量以及表达式进行优化
    • -O2 相对于-O1来说,增加了编译时间的基础上,提高了生成代码的执行效率
    • -Os 在-O2的基础上,去掉了导致最终可执行程序增大的优化,可以生成执行效率较高并且更小的执行程序
    • -O3 延长编译时间,在-O2基础上进行更多的优化,编译成的软件将生成更大体积更耗内存的二进制文件,增加编译失败的机会并且产生不可预知的程序行为,不建议使用
    • -Ofast 在-O3基础上添加一些非常规优化,通过打破一些国际标准来实现的,不仅以使用
    • -Og 在-O1的基础上去掉了一些影响调试的优化,如果最终为了调试可以使用这种优化方式
  6. -E 只做预处理而不编译

  7. -S 生成汇编代码
  8. -l 链接,告诉编译器去哪里寻找头文件,参数后紧跟库的名字告诉编译器要链接的库
    • -lpthread 代表使用pthread库实现多线程,代表使用C++11的线程库
    • lm 代表使用数学库
  9. -L 告诉编译器去哪里寻找需要的库文件,但是即使库文件就在当前目录下,编译器也不会去找的,所以必须加此参数,除非你的库在默认的寻找目录里,默认目录可以用gcc -print-search-dirs查看。
  10. -v 可以详细看到编译的过程
  11. -share 这个选项将尽量使用动态库,生成文件较小,但是需要动态库
  12. -static 这个将禁止使用动态库,编译出来的东西很大,不需要动态链接库就可以运行
    • static-libgcc 静态链接,编译时使用,用于生成独立的可执行文件
  13. -f 有点怪没找到,好像是一些配置 ^-^?
    • fPIE与-fpie是等价的。这个选项与-fPIC/-fpic大致相同,不同点在于:-fPIC用于生成动态库,-fPIE用与生成可执行文件。再说得直白一点:-fPIE用来生成位置无关的可执行代码。
    • fPIC与-fpic都是在编译时加入的选项,用于生成位置无关的代码(Position-Independent-Code),生成动态库。这两个选项都是可以使代码在加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表(GOT)来实现。-fPIC和-fpic最大的区别在于是否对GOT的大小有限制。-fPIC对GOT表大小无限制,所以如果在不确定的情况下,使用-fPIC是更好的选择
    • openmp 代表使用OpenMP多线程库
    • stack-protector-strong 启用栈保护功能
  14. -M 可以自动分析目标文件和源文件的依赖关系
  15. -MM 可以自动分析目标文件和源文件的依赖关系,它将忽略由 #include<file> 造成的依赖关系
  16. std=c++11 代表使用C++11的规范
  17. -D 代表定义一个宏,例如-DDEBUG
  18. -U 代表取消一个宏,例如-UDEBUG
  19. Werror 代表将所有警告视为错误
  20. Wno-* 代表取消某一类警告,例如-Wno-unused-variable表示取消未使用变量的警告
    • Wno-format-nonliteral 忽略对printf格式化字符串的检查
    • Wno-unused-parameter 忽略未使用的参数
  21. Wl,代表将传递给连接器,例如-Wl,-rpath=./指定运行时动态链接库的搜索路径为当前目录
    • Wl,—no-as-needed 静态链接,编译时使用,告诉编译器必须链接所有的库
  22. Wformat=2 检查printf格式化字符串的正确性
  23. Wconversion 检查隐式类型转换
  24. Wsign-conversion 检查有符号和无符号类型之间的转换
  25. -s 从可执行文件中删除所有符号表和重定位信息.

gcc编译过程

  1. 预处理

    将源文件处理为 .i/.ii 文件,处理各种预处理的指令,例如一些宏定义等

  2. 编译

    将预处理后的 .i/.ii 文件编译为 .S/.asm 文件,也就是汇编语言文件

  3. 汇编

    .S/.asm 文件处理为 .o 文件,也就是把汇编文件处理为机器码

  4. 链接

    将各种依赖的静态库/动态库文件, .o 文件,启动文件链接成最终可执行文件或者库文件

预处理

1
gcc -E a.c -o a.i

编译

1
2
gcc -S a.c
gcc -S a.i

汇编

1
2
gcc -c a.c
gcc -c a.s

第二条处理汇编语言的指令,实际上是使用汇编器 as.s 文件处理为 .o 文件的

1
as a.s -o a.o

链接

其中有两个参数 -L-l 用于指定链接的文件的地址和名称,使用 -l 可以省略库文件开头的 lib 和后缀

1
gcc a.c -Ldir -lname

查看gcc编译过程

使用 -v 来查看

1
gcc a.c -v