加-g才能用gdb调试,因为-g在二进制中嵌入dwarf调试段(如.debug_info、.debug_line),使gdb能将指令地址映射回源码行号、变量名和调用栈;不加则仅剩汇编,list报“no symbol table loaded”,print失败。

-g 参数就是开启调试信息的开关,不加它,GDB 就看不到源码行号、变量名、函数调用栈——根本没法做源码级调试。
为什么加了 -g 才能用 GDB 调试
默认编译(gcc test.c -o test)生成的可执行文件里只有机器指令,没有源码映射关系。GDB 需要靠 -g 写入的 DWARF 调试段(比如 .debug_info、.debug_line)才能把内存地址反查到 test.c:12 这样的位置。
- 没加
-g:GDB 启动后只能看到汇编,list命令报错 “No symbol table loaded”,print var直接失败 - 加了
-g:GDB 可以break main、step单步、print sum查变量值 - 注意:
-g不影响运行时行为,也不把源文件塞进二进制里——调试时必须保证当前目录有原始.c文件,否则 GDB 会提示 “No such file or directory”
不同 -g 级别怎么选
-g 实际是 -g2 的简写,但 GCC 提供了更细粒度控制:
-
-g1:只保留调用栈信息(backtrace可用),不记录局部变量,适合快速验证崩溃点,生成文件最小 -
-g(即-g2):标准选项,含函数/变量名、类型、行号,90% 场景够用 -
-g3:额外包含宏定义(#define DEBUG这类),调试宏逻辑时有用,但文件体积明显增大 -
-g0:显式关闭调试信息,等价于不加-g,常用于发布构建
实测:一个简单程序加 -g3 比 -g 多出 20%~30% 文件大小,但对运行速度和内存占用无影响——调试段在加载时不进内存。
常见错误:加了 -g 还是调试不了
不是加了 -g 就万事大吉,这几个坑最常踩:
- 只给部分源文件加
-g:比如gcc -g main.c util.c -o app正确;但gcc main.c -g util.c -o app会导致util.o没调试信息,GDB 进入util.c函数就变成汇编 - 链接时丢掉了调试信息:如果用了
strip或者构建脚本里执行了objcopy --strip-debug,-g白加 - 优化干扰:
-O2或更高优化等级会重排指令、内联函数、删变量,导致 GDB 显示 “value optimized out”。调试时建议搭配-O0或-Og(专为调试优化) - 路径问题:GDB 默认按编译时的绝对路径找源文件。如果移动了项目目录,可用
directory /path/to/src告诉 GDB 新位置
实际编译命令怎么写
记住这三条铁律:
- 所有参与链接的
.c文件,编译成.o时都得带-g:gcc -g -c main.c -o main.o、gcc -g -c util.c -o util.o,再gcc main.o util.o -o app - 一步到位也行:
gcc -g -O0 main.c util.c -o app(-O0关闭优化,避免变量消失) - 别和
-s(strip)或--strip-all同时用,它们会直接删掉.debug_*段
调试前用 readelf -S app | grep debug 确认输出里有 .debug_info 等节区,这是最直接的验证方式。











