深入探究Linux编译器gcc/g++:从基础到进阶
目录
一、编译的幕后流程
(一)预处理:宏与文件的魔法融合
(二)编译:代码规范性的严格审视
(三)汇编:迈向机器语言的关键一步
(四)连接:程序成型的最后拼图
二、函数库:静态库与动态库的抉择
(一)静态库:打包的完整力量
编辑(二)动态库:灵活的运行时加载
三、gcc选项:定制编译的魔法咒语
(一)基本编译控制选项
(二)链接选项
(三)调试与优化选项
(四)警告控制选项
(五)debug与release
在Linux编程的领域中, gcc/g++ 编译器是程序员们不可或缺的得力工具。它们如同精密的工匠,将我们编写的代码雕琢成可在Linux系统上高效运行的程序。今天,就让我们深入了解 gcc/g++ 的奥秘。
一、编译的幕后流程
在使用 gcc/g++ 之前,了解编译的基本流程至关重要。编译过程可分为四个主要阶段:
(一)预处理:宏与文件的魔法融合
预处理就像是一场代码的前期加工盛宴。它主要负责宏替换,比如将我们定义的 #define PI 3.14159 ,在代码中所有出现 PI 的地方替换为 3.14159 。同时,它还处理文件包含,像 #include <stdio.h> ,会把 stdio.h 头文件的内容插入到代码中相应位置。此外,条件编译(如 #ifdef 、 #else 、 #endif )的判断和注释的去除也在此阶段完成。使用 gcc 的 -E 选项,如 gcc -E hello.c -o hello.i ,可以让编译器在预处理结束后就停下,生成经过预处理的 hello.i 文件。
cd /usr/include前往头文件存放点
(二)编译:代码规范性的严格审视
进入编译阶段, gcc 就像一位严谨的语法老师,仔细检查代码的规范性。它会揪出语法错误,比如括号不匹配、变量未声明等问题。在确认代码无误后, gcc 会把经过预处理的代码转换成汇编语言。我们可以通过 -S 选项来生成汇编代码,例如 gcc -S hello.i -o hello.s ,此时得到的 hello.s 就是汇编语言版本的代码。
(三)汇编:迈向机器语言的关键一步
汇编阶段,编译器将汇编语言代码进一步转化为机器能够识别的二进制目标代码。使用 -c 选项,如 gcc -c hello.s -o hello.o ,就能生成二进制目标文件 hello.o 。此时,代码已经从人类可读的汇编语言变成了机器能直接处理的语言形式。
但是汇编后乱码不具备可读性,可通过od 实现汇编文件的二进制翻译
(四)连接:程序成型的最后拼图
连接阶段是将多个目标文件以及所需的库文件组合在一起,生成最终的可执行文件或库文件。通过 gcc hello.o -o hello ,这里的 -o 选项指定输出文件名,执行该命令后,就得到了可以运行的 hello 程序。
二、函数库:静态库与动态库的抉择
系统只提供动态库,类似于提供链接但不能运行,自行去安装静态库或动态库
(一)静态库:打包的完整力量
C语言静态库安装
C++静态库安装
静态库在编译链接时,会把库文件的所有代码都嵌入到可执行文件中。这就好比把所有需要的工具都直接放进一个大箱子里,箱子虽然变大了,但运行时不再需要额外寻找工具。
静态库的后缀名一般是 .a 。由于所有代码都在可执行文件中,所clear以生成的文件相对较大,但好处是运行时不依赖外部库,独立性强。
两库的抽象比喻

(二)动态库:灵活的运行时加载
动态库则不同,在编译链接时,它不会把库文件代码全部加入可执行文件,而是在程序执行时,由运行时链接文件去加载库。这就像需要工具时再去工具房拿,可执行文件这个“箱子”就不需要装太多东西,体积相对较小。
动态库后缀名通常是 .so ,像 libc.so.6 就是常用的动态库。 gcc 默认生成的二进制程序是动态链接的,可以用 file 命令来验证。
三、gcc选项:定制编译的魔法咒语
gcc 提供了丰富多样的选项,让我们能够根据需求定制编译过程:
(一)基本编译控制选项
- -E :仅进行预处理,不生成文件,需重定向输出,如 gcc -E source.c -o preprocessed.i 。
- -S :编译到汇编语言,不进行汇编和链接,生成汇编文件,例如 gcc -S preprocessed.i -o assembly.s 。
- -c :编译到目标代码,生成目标文件,像 gcc -c assembly.s -o object.o 。
- -o :用于指定输出文件的名称, gcc object.o -o output_program 。
(二)链接选项
- -static :采用静态链接,将所有库代码都嵌入可执行文件。
- -shared :优先使用动态库进行链接,生成文件较小,但依赖系统中的动态库。
(三)调试与优化选项
- -g :生成调试信息,方便使用 gdb 等调试工具进行调试。
- -O0 、 -O1 、 -O2 、 -O3 :编译器的优化选项,从无优化到最高级别优化, -O1 为默认优化级别。
(四)警告控制选项
- -w :不生成任何警告信息,让编译过程“安静”进行。
- -Wall :生成所有可能的警告信息,帮助我们发现代码中潜在的问题。
(五)debug与release
系统默认执行release编译,想以debug执行编译要在结尾加'-g',通常debug版本由于添加了debug调试信息,通常都是比默认生成的release版本的所占用的空间大。
readelf -S 是Linux下 readelf 命令的一个选项,用于显示ELF(可执行与可链接格式)文件的节头表(Section Header Table)信息。以下是对它的详细介绍:
作用(这里可以检测出文件编译是由release变为debug后空间内存的扩大)
节头表描述了ELF文件中各个节的属性,包括节的名称、类型、大小、偏移量等信息。 readelf -S 主要用于帮助开发者和分析人员了解ELF文件的内部结构,特别是各节的相关信息,这对于理解程序的链接、加载过程以及进行逆向工程等都非常有帮助。
输出信息示例
- 第一行命令: readelf -S mytest | grep -i debug ,是先使用 readelf -S 获取 mytest 这个ELF文件的节头表信息,然后通过管道( | )将这些信息传递给 grep , grep 在接收到的信息中搜索包含“debug”(不区分大小写)的行。目的是在 mytest 文件的节头表信息里筛选出和“debug”相关的内容。
- 第二行命令: readelf -S mytest_debug | grep -i debug ,原理和第一行类似,只是这里操作的对象是 mytest_debug 这个ELF文件,同样是获取其节头表信息,并筛选出包含“debug”(不区分大小写)的行。
因为第一行不是debug编译,所以查找失败,但第二个是debug编译,所以不受影响。
readelf -S 为我们查找编译文件属性提供了更快捷的方法
gcc/g++ 编译器有着丰富的功能和灵活的配置选项。无论是处理简单的小程序,还是构建复杂的大型项目,深入理解这些知识都能让我们在Linux编程中更加游刃有余,编写出高效、稳定的代码。希望大家通过不断实践,熟练掌握 gcc/g++ 的使用,在编程之路上不断进步!