必须加-g参数才能单步调试,否则gdb缺乏源码映射信息,step和next会跳过函数或退入汇编;-g嵌入dwarf调试段(如.debug_info),使gdb能将地址映射到源码行号、变量名和调用栈。

gdb 启动时必须带 -g 参数才能单步调试
不加 -g 编译出来的二进制文件没有调试信息,step 和 next 会直接跳过函数、显示 Single stepping until exit from function xxx,甚至退回到汇编级别。这不是 gdb 坏了,是编译器根本没告诉它源码在哪。
正确做法:g++ -g -o myapp main.cpp
然后 gdb ./myapp
- 如果用了 CMake,确保
CMAKE_BUILD_TYPE是Debug,或手动加set(CMAKE_CXX_FLAGS_DEBUG "-g") - 用
file myapp在 gdb 里检查是否加载了调试符号:看到Reading symbols from myapp...才算成功 - Clang 用户同理,用
clang++ -g
step 和 next 的本质区别在“是否进入函数内部”
step(缩写 s)会进入当前行调用的函数;next(缩写 n)则把整行当作一个原子单位执行完,不进函数体。这个区别在有函数调用的语句上特别明显:
int x = foo() + bar(); // 这一行,step 会先停在 foo() 入口,next 则直接执行完这一整行
- 如果当前行没有函数调用(比如纯赋值、if 判断),
step和next行为一致 -
step遇到库函数(如std::vector::push_back)且没装 debuginfo 包时,会卡在汇编里——这时按finish退出当前函数更实际 - 想跳过某个函数但又不确定它会不会崩溃?先
next,再用print或display检查变量变化
遇到 inline 函数或模板实例化,step 可能“失效”
编译器内联后,源码行和机器指令不再一一对应。step 可能直接跳到下一行,或者停在你完全没写过的临时变量初始化位置(尤其是 std::string、std::vector 构造)。这不是 bug,是调试信息丢失导致的映射模糊。
- 用
info sources确认当前文件是否被正确识别;用list看 gdb 认为的当前上下文是否匹配你预期的代码段 - 加
-fno-inline临时禁用内联(仅调试用),让step行为更可预测 - 对模板-heavy 的代码,
next比step更可靠;真要钻进去,优先设断点在具体实例化的函数名上,比如break std::vector<int>::push_back</int>
stepi / nexti 是汇编级单步,别误当 step 的“加强版”
stepi(缩写 si)和 nexti(缩写 ni)操作的是 CPU 指令,不是 C++ 语句。它们在你怀疑编译器优化出错、或调试 asm 内联代码时才有意义。日常用 step/next 就够了。
- 开了
-O2后,stepi显示的指令顺序可能和源码逻辑严重不符——这时候该关优化,而不是硬啃汇编 -
disassemble命令能看到当前函数的汇编,配合stepi时注意看$pc(程序计数器)指向哪条指令 - 误用
stepi容易陷入 libc 或系统调用内部,用finish或until快速跳出比反复stepi更高效
step 和 next 就永远不像文档里写的那么“听话”。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











