GCC 使用备忘录
目录一、GCC 的“黑盒”内部编译的四个阶段二、核心选项速查表实战一分步编译与 -c, -o实战二链接库文件与 -L, -l实战三调试与警告与 -Wall, -g实战四宏定义与 -DGCC (GNU Compiler Collection)不仅仅是一个 C 语言编译器它是 GNU 项目下的编译器套件支持 C、C、Objective-C、Fortran、Ada 等多种语言。在嵌入式和 BSP 领域我们最常使用的是它的交叉编译版本如arm-linux-gnueabihf-gcc即在 x86 电脑上编译出能在 ARM、MIPS 或 RISC-V 架构开发板上运行的代码。一、GCC 的“黑盒”内部编译的四个阶段预处理 (Preprocessing)动作处理#include、#define等宏指令展开头文件删除注释。产物.i文件。编译 (Compilation)动作进行语法分析、优化将代码转换为汇编语言。产物.s文件。汇编 (Assembly)动作将汇编代码翻译成机器码二进制。产物.o(目标文件)。注意此时还是不可执行的因为还没有地址重定位。链接 (Linking)动作将多个.o文件和库文件.a或.so拼在一起分配最终地址。产物可执行文件如a.out或指定的文件名。二、核心选项速查表选项含义使用场景-o 文件名指定输出文件名不写默认生成a.out一定要养成指定名字的习惯。-c只编译、汇编不链接多文件编译时生成.o目标文件。-I 目录指定头文件搜索路径当头文件不在当前目录或系统默认路径时使用。-L 目录指定库文件搜索路径链接非系统路径下的库文件时使用。-l 库名链接指定的库如-lpthread链接线程库-lm链接数学库。-g生成调试信息需要用 GDB 调试时必加否则看不到变量值。-Wall显示所有警告强烈建议每次都加能帮你发现未使用的变量等隐患。-O2开启二级优化代码交付时的标准配置提升运行速度。-D 宏定义宏相当于代码里的#define常用于条件编译如-DDEBUG。实战一分步编译与-c,-o场景项目很大修改了main.c但不想重新编译整个项目。我们需要先单独把源文件变成目标文件.o。# 1. 将库源码编译为目标文件 gcc -c math_lib.c -o lib_math.o # 2. 将主程序编译为目标文件 gcc -c main.c -o main.o如果不加-cGCC 会尝试进行链接并生成可执行文件因为库文件通常没有入口点所以会报错找不到main函数加上-c后GCC只进行编译和汇编跳过链接检查因此不会报错并生成包含机器码但尚未链接的目标文件.o注意此时如果用./main.o运行系统会提示“权限不够”或“无法执行二进制文件”因为它还没链接。实战二链接库文件与-L,-l场景我们要把刚才生成的main.o和lib_math.o拼在一起生成最终的可执行程序。为了演示-L和-l我们假设库文件在一个名为build的子文件夹里且库文件名为libmath.aLinux 库的标准命名。# 假设我们将 lib_math.o 打包成了静态库 libmath.a 放入了 build 目录 ar rcs build/libmath.a lib_math.o # 使用 -L 指定 build 目录使用 -l 指定 math (自动补全 lib 和 .a) gcc main.o -L./build -lmath -o calculator效果展示如果不加-L./buildGCC 会报错cannot find -lmath因为它只会在系统默认路径如/usr/lib找找不到你自定义的目录。如果不加-lmath链接器会报错undefined reference to add因为它知道有main.o但不知道add函数在哪里。加上后成功生成calculator文件运行./calculator即可看到程序输出结果。-name→libname.a (或) .so你输入了-lmathGCC 自动加上lib前缀变成libmathGCC 自动加上.a(静态库) 或.so(动态库) 后缀变成libmath.a或libmath.soar这是Archiver归档工具的缩写。它是 Linux/Unix 系统下用来管理静态库文件也就是.a文件的核心工具。r(Replace)表示插入或替换。它的作用是将后面的.o文件插入到库文件中如果库中已经存在同名文件则进行替换。c(Create)表示创建。告诉工具如果指定的库文件build/libmath.a不存在就新创建一个而且不会给你弹出一堆警告信息。s(Symbol)表示生成索引。这会生成一个符号表Symbol Table相当于给库里的函数和变量建立一个“目录”。有了这个索引编译器在链接时查找函数的速度会快很多。部分含义作用ar工具名调用归档程序rcs参数组合r: 插入/替换文件c: 创建新库不报警告s: 生成索引加速链接build/libmath.a目标文件最终生成的静态库名称通常以lib开头.a结尾lib_math.o源文件需要被打包进库的编译好的目标文件实战三调试与警告与-Wall,-g场景代码里有个变量定义了但没使用而且你想用 GDB 调试。#include stdio.h int main() { int a 10; int b 20; // 故意没使用 b模拟警告 printf(Result: %d\n, a); return 0; }命令对比1. 不加警告选项默认模式gcc main.c -o app_silent #效果静默通过没有任何提示。隐患被埋下了。2. 加上-Wall推荐模式gcc -Wall main.c -o app_warn #效果终端立即打印警告信息 #main.c: In function ‘main’: #main.c:4:9: warning: unused variable ‘b’ [-Wunused-variable] # 4 | int b 20; # | ^3. 加上-g调试模式gcc -g main.c -o app_debug效果生成的文件体积变大因为插入了调试符号。app_silent大小16KBapp_debug大小24KBGDB 演示如果不加-g运行gdb ./app_silent当你查看变量时GDB 会提示No symbol table is loaded没有符号表。加上-g后GDB 可以精确显示print b的值为 20并定位到具体的代码行号。实战四宏定义与-D场景同一份代码想在“调试版”打印日志在“发布版”关闭日志而不想修改代码。#include stdio.h int main() { #ifdef DEBUG printf( [DEBUG] 程序开始运行...\n); #endif printf(Hello World\n); return 0; }命令对比1. 普通编译gcc main.c -o app_normal ./app_normal #hello world2. 使用-D定义宏gcc -DDEBUG main.c -o app_debug_mode ./app_debug_mode # [DEBUG] 程序开始运行... #hello world