-g是开启调试的最低门槛,但需配合-O0禁用优化才能保证变量可观察,-g1仅支持栈回溯,-g2(即-g)含函数名、行号和局部变量信息,-g3增加宏定义等完整调试数据,GCC 14默认生成DWARF-5格式。

gcc -g 是开启调试的最低门槛,但不是万能钥匙
直接加 -g 就能用 GDB 调试,但很多问题出在“能启动”和“能看清变量”之间。关键不是加不加 -g,而是加对哪一级、配什么编译策略。
常见错误现象:gdb ./app 能进,break main 能设,但 print i 显示 <optimized out></optimized>;或者 info locals 为空;甚至 step 跳过整行、栈帧显示为 (??)。
-
-g本身只保证基础符号存在,不保证变量可观察 —— 必须配合-O0(禁用优化) - 如果用了
-O1或更高,即使加了-g3,局部变量也可能被寄存器重用或合并,GDB 无法还原原始语义 -
-fno-omit-frame-pointer建议始终加上,尤其在启用优化时(如-O2 -g),否则bt可能断在中间帧
选哪个 -g 级别?看你要查什么
不是越高级别越好,-g3 会显著增大二进制体积(实测 +65%),且仅在特定场景有用。
- 日常开发调试:用
-g(等价于-g2)足够 —— 包含函数名、行号、局部变量名和类型 - 需要展开宏看预处理结果:必须
-g3,配合 GDB 的info macro MACRO_NAME - 调试内联函数或模板实例:
-g3+-gdwarf-5(GCC 14 默认,老版本需显式指定)能更好还原调用上下文 - 发布前剥离调试信息:用
strip --strip-debug,它只删.debug_*段,不影响运行时行为
GCC 14 下的 DWARF 版本选择很关键
默认用 -g 生成的是 DWARF-5(GCC 14),但旧版 GDB(optimized out 或类型信息缺失。
- 兼容性优先:加
-gdwarf-4,例如gcc-14 -g -gdwarf-4 -O0 -o app main.c - 要精确追踪作用域和内联展开:坚持用
-gdwarf-5,但确保 GDB ≥13.1 - 验证当前文件用的 DWARF 版本:
readelf -p .debug_abbr app | head -n 5,看开头是否含DW_FORM_strp(DWARF-5 特征)
别漏掉预处理器定义和调试辅助开关
光有 -g 不等于调试就通了。很多逻辑依赖宏控制,而默认 GDB 看不到宏是否生效。
- 用
-DDEBUG或-DVERBOSE编译,再配合源码里的#ifdef DEBUG分支,才能让关键日志/断言/路径暴露出来 - 加
-ggdb(非必需但推荐):告诉 GCC 为 GDB 生成额外友好信息,比如更准确的内联位置描述 - 若怀疑内存问题,叠加
-fsanitize=address,它和-g完全兼容,错误报告里会带源码行号
最易被忽略的点:-g 生成的调试段不加载进内存,所以运行时无开销,但你得记得——每次改代码后重新编译,否则 GDB 加载的是旧符号,行号对不上、变量名错乱都是常态。


















