3. GDB基本命令:run、continue、next、step、print、list、quit

调试器这东西,说白了就是让你能「看」着程序跑。GDB 最核心的几条命令,其实一只手就数得过来。我刚开始学的时候,就靠这七条命令撑过了大半个项目。你想想看,如果连程序都跑不起来,那还调试个啥?

3.1 run —— 让程序跑起来

run 命令,简写是 r。它的作用就是启动你正在调试的程序。如果你在启动 GDB 时没带参数,或者想重新跑一次,就用它。

基本用法:

(gdb) run
(gdb) r          # 简写形式
(gdb) run arg1 arg2   # 带命令行参数启动

我个人习惯在启动 GDB 时先设好断点,然后再敲 run。这样程序一跑起来就会停在你想看的地方。如果你没设断点,那程序就直接跑完了——嗯,那就没调试什么事了。

小技巧:如果你忘了之前设过哪些断点,可以用 info breakpoints 查看。我经常这么干,因为年纪大了记性不好。

3.2 continue —— 继续跑,别停

continue,简写 c。当程序因为断点停下来后,你想让它继续往下跑,就用这个命令。

(gdb) continue
(gdb) c          # 简写

我在项目中遇到过一种情况:一个死循环问题,我在循环体里设了断点,然后不停地 c,观察变量每次的变化。说实话,这方法虽然笨,但有时候真管用。

注意:如果程序已经跑完了,你再敲 continue,GDB 会告诉你「程序已退出」。这时候你得用 run 重新启动。

3.3 next —— 一步一步来

next,简写 n。它的特点是「不进入函数内部」。说白了,就是遇到函数调用时,直接跳过,把整个函数当成一行执行完。

(gdb) next
(gdb) n          # 简写

举个例子:

int main() {
    int a = 10;
    int b = 20;
    int c = add(a, b);  // 如果在这里用 next,会直接得到结果,不进入 add 函数
    printf("%d\n", c);
    return 0;
}

当你停在 int c = add(a, b); 这一行时,敲 n,程序会直接执行完 add 函数,然后停在下一行 printf 那里。你想想看,如果你只是想看最终结果,不想关心 add 内部怎么算的,用 next 就对了。

3.4 step —— 钻进去看

step,简写 s。和 next 相反,它会「钻进」函数内部。

(gdb) step
(gdb) s          # 简写

还是上面那个例子,如果你在 int c = add(a, b); 这一行敲 s,GDB 会跳进 add 函数的第一行代码。我曾经在调试一个内存泄漏问题时,就是靠 step 一步步跟进 malloc 的调用链,才发现有个指针没释放。

核心区别:

命令 行为 适用场景
next (n) 不进入函数 你信任这个函数,只想看外部逻辑
step (s) 进入函数 你想检查函数内部实现

3.5 print —— 看看变量值

print,简写 p。调试的核心就是看变量,没有 print 你啥也干不了。

(gdb) print var_name
(gdb) p var_name
(gdb) p/x var_name   # 十六进制显示
(gdb) p/d var_name   # 十进制显示
(gdb) p &var_name    # 打印变量地址

我个人最常用的就是 p/x,因为嵌入式开发里经常要看寄存器值,十六进制更直观。有一次我调试一个串口驱动,数据老是发不对,用 p/x 一看,发现有个标志位被意外清零了——嗯,这种问题用眼睛看代码是看不出来的。

进阶用法:

  • p *ptr —— 打印指针指向的内容
  • p array[0]@10 —— 打印数组前10个元素
  • p (struct foo *)0x1000 —— 把某个地址强制转换成结构体指针并打印

3.6 list —— 看看源代码

list,简写 l。显示当前行附近的源代码。默认显示10行。

(gdb) list
(gdb) l          # 简写
(gdb) l 20       # 显示第20行附近
(gdb) l main     # 显示main函数附近的代码
(gdb) l func_name:30  # 显示func_name函数第30行附近

为什么需要 list?因为有时候你停在某个断点,光看行号根本想不起来这行是干嘛的。敲一下 l,上下文就出来了。我习惯在调试前先用 list 扫一遍代码,确认断点位置对不对。

注意:如果编译时加了 -g 选项但没有加 -O0,编译器可能会优化代码,导致 list 显示的行号和实际执行的不一致。我曾经被这个坑过,查了半天才发现是优化搞的鬼。

3.7 quit —— 退出调试

quit,简写 q。退出 GDB。

(gdb) quit
(gdb) q          # 简写

如果你在调试过程中想退出,直接敲 q 就行。如果程序还在运行,GDB 会问你是否确认退出。这时候敲 y 就行。

避坑指南:我曾经在调试一个服务器程序时,忘了先停掉程序就直接 quit,结果后台进程还在跑,占着端口不放。后来我学乖了,退出前先用 kill 或者 detach 把程序处理干净。

知识体系总览

下面这张图把这七条命令的关系梳理了一下,你一看就明白:

GDB 七条基本命令关系图 GDB 调试会话 run (r) — 启动程序 断点命中,程序暂停 list (l) — 查看源代码 next (n) 不进入函数 step (s) 进入函数 print (p) — 查看变量 continue (c) — 继续执行 quit (q) — 退出调试

这张图把调试流程串起来了:先用 run 启动,断点命中后,你可以用 list 看代码、用 print 看变量、用 next 或 step 单步执行。搞定了就 continue 继续跑,最后 quit 走人。说白了,调试就是这么回事。

我的建议:刚开始学的时候,别急着背命令。打开一个简单的 C 程序,设个断点,然后把这七条命令挨个试一遍。用不了十分钟,你就能记住。我当年就是这么干的,到现在肌肉记忆还在。

可可精品资料库