-g是开启调试信息的唯一必需选项,不加则GDB无法显示源码、行号和变量;gcc -g编译必须配-O0,否则优化会导致“value optimized out”或断点异常;开发期最安全组合为gcc -g -O0 -o app app.c。

-g 是开启调试信息的唯一必需选项,不加它,GDB 就看不到源码、行号和变量。
gcc -g 编译时必须配 -O0
优化会重排指令、内联函数、删掉未使用变量,导致 GDB 显示“value optimized out”或断点跳到奇怪位置。即使只是临时调试,也别信“-O1 影响不大”这种经验——实际中 -O1 就可能让局部变量消失。
-
gcc -g -O0 -o app app.c是开发期最安全的组合 - 如果必须带优化(比如复现某个特定场景),至少用
-O0 -g先确认逻辑正确性,再逐步加优化测试 -
-Og是 GCC 4.8+ 引入的“调试友好型优化”,但并非万能:它仍可能折叠表达式或改变栈帧结构,对复杂指针操作或内联汇编支持不稳定
调试信息级别选 -g 还是 -g3?
-g(等价于 -g2)覆盖绝大多数调试需求:函数名、参数、局部变量、行号映射都完整。除非你真要展开宏定义看预处理结果,否则 -g3 只是增大文件体积、拖慢编译速度,且部分旧版 GDB 对宏调试支持有限。
- 日常开发、CI 构建调试包:用
-g - 需要在 GDB 里
info macro XXX查宏展开:才加-g3 -
-g1仅保留调用栈回溯能力,适合嵌入式资源紧张环境,但无法查看变量值
调试符号要不要分离?用 -ggdb 还是 -g?
-ggdb 和 -g 生成的调试信息内容基本一致,区别在于 -ggdb 启用更多 GDB 专有扩展(如更细粒度的变量作用域标记),但代价是兼容性略低——某些非 GNU 工具链(如 LLDB 或 Windows 上的 lldb-mi)可能解析异常。分离调试符号(objcopy --only-keep-debug)只在发布场景有意义:你得先用 -g 编译出含调试信息的二进制,再抽出来存为 .debug 文件,否则 GDB 加载时找不到符号。
- 本地开发直接用
gcc -g -O0,别折腾分离 - 构建发布包时想减小主程序体积:先
gcc -g -o app app.c,再objcopy --only-keep-debug app app.debug,最后objcopy --strip-debug app -
-gdwarf-5在 GCC 14+ 是默认,无需显式加;若需兼容老旧 GDB(-gdwarf-4
最容易被忽略的一点:调试信息只存在于编译阶段决定的二进制里,运行时无法动态补上。哪怕你后期发现没加 -g,也必须重新编译——没有“热加载调试符号”这回事。


















