-g是GDB调试的必要前提,必须在编译目标文件(.o)时添加,用于嵌入源码行号、变量名、调用栈等调试信息;链接阶段无需重复加,但所有参与链接的.o及库均需带-g,且应避免与-O2/O3混用,推荐-g -O0组合。

gcc -g 是 debug 调试的前提,不是可选项
没加 -g 编译出来的二进制文件,GDB 里看不到源码行号、变量值、函数调用栈,基本等于“盲调”。make 本身不自动加 -g,它只按 Makefile 规则执行命令——如果规则里没写,生成的就是 release 版本。
常见错误现象:gdb ./app 启动后 list 显示“No symbol table loaded”,print var 报错 “No symbol ‘var’ in current context”。
- 必须在编译目标文件(
.o)时就带上-g,例如:gcc -g -c main.c -o main.o - 链接阶段不需要重复加
-g,调试信息已嵌入.o中;但若链接多个.o,每个都得带-g - 不要和
-O2或-O3混用:优化会重排代码、内联函数、删变量,导致 GDB 显示行为与源码不一致(-O0 -g最稳妥)
Makefile 里怎么安全地支持 debug / release 切换
硬编码 -g 会导致每次 make 都生成带调试信息的大文件,发布时还得手动改 Makefile。更合理的是用变量控制。
示例写法:
DEBUG ?= 1
CFLAGS = -Wall -I./include
ifeq ($(DEBUG),1)
CFLAGS += -g -O0
else
CFLAGS += -O2 -DNDEBUG
endif
app: main.o utils.o
gcc $(CFLAGS) main.o utils.o -o app
这样就能通过命令行灵活切换:
-
make→ 默认 debug 版(DEBUG=1) -
make DEBUG=0→ release 版,无调试信息,开启优化 -
make DEBUG=1 CFLAGS="-g -O1"→ 手动覆盖 CFLAGS(注意引号)
静态库和动态库的 debug 信息要单独处理
如果你的项目用了自建静态库(libxxx.a)或动态库(libxxx.so),仅给主程序加 -g 不够——库里的函数照样无法单步进入、看不到局部变量。
关键点:
- 静态库:每个
.o文件生成时就得带-g,再用ar打包,例如:gcc -g -c add.c -o add.o && ar rcs libcalc.a add.o - 动态库:必须加
-fPIC,且-g要写在生成.o和.so的两步里:gcc -g -fPIC -c add.c→gcc -g -shared add.o -o libcalc.so - 验证方法:
readelf -S libcalc.so | grep debug,有.debug_*段说明成功
gdb 加载路径问题常被忽略
Makefile 里用 -L./ -lxxx 链接本地库,不代表 GDB 运行时能自动找到这些库的调试符号——尤其动态库,GDB 默认只查系统路径(/usr/lib 等)。
运行前必须设置环境变量:
- 对动态库:
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH(否则gdb ./app启动就报 “shared library not found”) - 对源码路径:如果
.o编译时用了相对路径(如gcc -g main.c),GDB 通常能定位;但如果 Makefile 在子目录执行、源码在上层,GDB 可能找不到main.c——这时加-fdebug-prefix-map更可靠:gcc -g -fdebug-prefix-map=$(PWD)=.
最麻烦的其实是混合场景:主程序 debug、静态库没 debug、动态库路径未导出——GDB 表现就是“能停断点但 print 不出变量”,得逐层确认每个环节是否真带了调试信息。


















