-g是gdb实现源码级调试的硬性前提,缺失则无法映射指令与源码行号、显示函数名和变量值;必须为所有目标文件添加-g,且推荐搭配-og而非高优化级别,并在发布前用strip移除调试信息。

-g 参数不是可选的“锦上添花”,而是 GDB 能否做源码级调试的硬性前提。 没加 -g 的可执行文件,GDB 只能看汇编、查寄存器、单步指令,完全看不到函数名、变量值、行号——等于在黑盒里摸电路板。
不加 -g 时 GDB 连 main 都找不到
常见错误现象:(gdb) run 启动后直接报 No symbol table is loaded;(gdb) list 显示空;(gdb) break main 提示 Function main not defined。
- GDB 依赖二进制中嵌入的
.debug_*节区(如.debug_info、.debug_line)来定位源码位置,这些节区只有-g开启时才生成 - 即使你有源文件,GDB 也根本不知道哪条机器指令对应哪一行 C 代码——没有
-g,就没有行号映射表 -
readelf -S ./a.out | grep debug查不到任何.debug_*节区,就说明-g没生效
-g1、-g2、-g3 的实际区别在哪
不是“越多越好”,而是按需选择:
-
-g1:只保留函数名、全局变量、行号表,不存局部变量 —— 适合快速确认 crash 发生在哪一行,但print i会报No symbol "i" in current context -
-g2(即默认-g):含完整局部变量、结构体成员、类型定义 —— 日常调试的黄金配置 -
-g3:额外包含宏定义(#define MAX 1024这类),体积更大,但对分析预处理行为有用;多数项目用不到
和 -O 优化一起用时的坑
加了 -g 但开了 -O2 或 -O3,GDB 可能显示“跳行”“变量优化掉”“无法访问”:
- 编译器重排指令、内联函数、删临时变量,导致源码行和机器指令不再一一对应
- 推荐组合:
-g -Og——-Og是专为调试设计的优化级别,保留调试友好性的同时做轻量优化 - 若必须用
-O2,至少补一句:-g -O2 -fno-omit-frame-pointer,否则栈回溯(bt)可能断裂
为什么 .o 文件也要加 -g
链接阶段不会凭空生成调试信息。如果只给最终链接命令加 -g,而中间的 .o 是用 gcc -c test.c(无 -g)生成的,那链接出来的可执行文件依然没调试信息。
- 必须确保每个参与链接的目标文件都带
-g:gcc -g -c test.c→test.o,再gcc -g test.o -o app - CMake 用户注意:
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og")要作用于所有构建阶段,不能只设在链接器 flags 里
最容易被忽略的一点:调试信息存在文件里,但运行时不加载——所以加 -g 几乎不影响程序启动速度或内存占用;但它会让二进制变大(实测常增 20%~50%),上线前记得用 strip 去掉,别把带 -g 的包直接扔到生产环境。











