-g3 -o0 是开发阶段最稳妥的调试编译组合:-g3 包含宏定义、预处理展开和内联函数源码映射,-o0 关闭所有优化以避免变量被删或语句重排,确保 gdb 能完整调试。

-g 是开启调试信息最基础、也最常用的命令,但光写 -g 往往不够——它只生成默认级别(等价于 -g2)的调试符号,对宏、内联函数、复杂作用域的追踪支持有限。真正能支撑完整源码级调试的,是组合使用。
gcc -g3 -O0 是开发阶段最稳妥的调试编译组合
绝大多数调试失败不是因为 GDB 不会用,而是编译时没给足信息或优化干扰了执行流:
-
-g3包含宏定义、预处理展开、内联函数源码映射,info macro和step进内联函数才有效 -
-O0关闭所有优化,避免变量被删、语句重排、寄存器复用——否则print var可能报"No symbol 'var' in current context" - 别省略
-Wall -Wextra:很多逻辑错误在编译期就能暴露,比进 GDB 后查更省时间
为什么 -g 有时不生效?常见原因和验证方法
现象:GDB 启动后显示 No debugging symbols found,或 list 命令看不到源码。
- 确认目标文件是否真含调试段:
readelf -S your_binary | grep debug—— 应看到多个.debug_*段 - 检查是否误用了
-s(strip 符号)或链接时加了-Wl,--strip-all,这些会直接删掉.debug_* - 多文件编译时,
-g必须出现在每个gcc -c命令里,不能只加在最后链接那步 - 如果用了 CMake,默认
Debug模式通常已设-g,但可手动检查get_property(DIRECTORY PROPERTY COMPILE_OPTIONS)
DWARF 版本选 -gdwarf-5 还是 -gdwarf-4?
GCC 14 默认输出 DWARF-5,但旧版 GDB(如 8.x 或某些嵌入式工具链)可能不完全支持。
- 本地开发且 GDB ≥ 10.2:直接用
-g -gdwarf-5,类型信息更准、内存布局描述更细 - 需要兼容老旧环境:显式降级
-g -gdwarf-4,避免Cannot find bounds of member function类报错 - 不用
-gdwarf-3或更低——GCC 14 已弃用,且缺乏对 C++20 概念、结构化绑定的完整支持
调试信息体积大?别盲目砍到 -g1
-g1 确实让二进制小 45% 以上,但它只保留函数名和行号,丢掉局部变量、宏、作用域信息——等于把 GDB 降级成带断点的 objdump。
- 开发机磁盘不是瓶颈,优先保调试能力;发布前再用
strip --strip-debug单独剥离调试段 - 若必须减体积,用
-g -grecord-gcc-switches替代-g1:保留关键路径信息,同时记录编译参数供回溯 - 注意:
-g3对静态库(.a)同样生效,但动态库(.so)需确保加载时路径正确,否则 GDB 找不到符号
-fno-omit-frame-pointer —— 它不产生调试符号,但能让 bt(backtrace)在优化开启时仍保持可用。哪怕你写了 -g3,没它,栈帧一乱,up/down 就容易跳错层。











