写点什么

C/C++ 生态工具链——gcc/g++ 编译器使用指南

作者:Revolution_z
  • 2022 年 9 月 15 日
    湖北
  • 本文字数:3241 字

    阅读完需:约 11 分钟

C/C++生态工具链——gcc/g++编译器使用指南

一,关于 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++举例

1.样例代码:

#include <iostream>
int main() { std::cout << "Hello World!" << std::endl; return 0;}
复制代码


2.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 告诉链接器优先使用动态库。



有关的环境变量:

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:列出依赖的动态库

nm:查看动态库/静态库中的函数


六,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...
复制代码


在 linux 环境使用:man g++可以查看 g++命令常用参数

常见参数如下(注意大小写): 

-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/

https://linuxhint.com/what-is-ld-library-path/

发布于: 刚刚阅读数: 3
用户头像

Revolution_z

关注

努力具有时效性,所以要不断努力 2020.02.15 加入

【微信公众号】背包客Revolution

评论

发布
暂无评论
C/C++生态工具链——gcc/g++编译器使用指南_c++_Revolution_z_InfoQ写作社区