2. GDB基础命令:run、break、next、step、continue、print、quit

好,咱们正式开始动手了。这一章我带你过一遍GDB最核心的七个命令。说白了,这七个命令就是你调试生涯的「七种武器」。我当年刚学GDB时,就靠这七个命令走天下,后来遇到再复杂的bug,也是从它们开始拆解的。

2.1 启动与运行:run

run命令,简写是r。它的作用就是启动你正在调试的程序。你想想看,GDB加载完程序后,程序还没跑起来呢,你得告诉它「开始吧」。

基本用法:

(gdb) run
(gdb) r          # 简写形式
(gdb) run arg1 arg2   # 带命令行参数启动
(gdb) run < input.txt # 重定向输入

我个人习惯在启动前先设好断点,然后再run。为什么?因为程序一旦跑起来,可能很快就结束了,你根本没机会打断它。我在项目中遇到过好几次这种尴尬——忘了设断点,程序直接跑完,白忙活一场。

小技巧:如果程序已经跑过一遍了,想重新跑,直接敲run就行。GDB会问你是否重启,回答y即可。或者你也可以用start命令——它相当于run加一个自动断点在main函数入口处。

2.2 设置断点:break

break,简写b。这是调试的灵魂。没有断点,调试就像蒙着眼睛找东西。

用法 示例 说明
按行号 break 42 在当前源文件的第42行停下
按函数名 break main 在main函数入口处停下
按文件名+行号 break foo.c:42 在foo.c的第42行停下
条件断点 break 42 if i == 5 当i等于5时才在第42行停下

条件断点这玩意儿特别实用。我曾经调试一个循环一万次的代码,每次都在同一个位置断住,手动按continue按到手抽筋。后来学会了条件断点,直接写b 42 if i == 9999,一步到位。

注意:断点设多了会影响程序运行速度。尤其是条件断点,每次循环都要判断条件,开销不小。我建议只设必要的断点,用完就删。

2.3 单步执行:next 与 step

这两个命令经常被搞混。我简单说一下区别:

  • next(n):单步执行,遇到函数调用会「跳过」,不进入函数内部。
  • step(s):单步执行,遇到函数调用会「进入」函数内部。

举个例子:

// 示例代码
void helper() {
    printf("hello\n");
}

int main() {
    int a = 10;
    helper();    // 假设断点停在这一行
    a = 20;
    return 0;
}

如果断点停在helper();这一行:

  • 敲next,直接跳到a = 20;那一行,helper函数执行完了你看不到内部细节。
  • 敲step,会跳进helper函数内部,停在printf("hello\n");这一行。

嗯,这里要注意:如果你不小心step进了某个你不关心的函数,可以用finish命令直接跑完当前函数,回到调用处。

2.4 继续执行:continue

continue,简写c。它的意思是「继续跑,直到遇到下一个断点或程序结束」。

这个命令看起来简单,但有个坑:如果你设了断点在循环里,每次continue都会停在同一个断点位置。这时候配合条件断点或者ignore命令会更好用。

实用场景:

(gdb) break 42
(gdb) ignore 1 5   # 忽略断点1的前5次命中
(gdb) continue      # 第6次命中时才会停下

我曾经调试一个网络服务的内存泄漏问题,断点设在分配内存的地方,每次continue都停在同一行。用ignore跳过前几百次分配,直接观察最后一次分配后的状态,效率高了很多。

2.5 查看变量:print

print,简写p。调试的核心就是看数据,print就是你的眼睛。

(gdb) print a          # 打印变量a的值
(gdb) p &a            # 打印变量a的地址
(gdb) p *ptr          # 打印指针指向的值
(gdb) p array[5]      # 打印数组第6个元素
(gdb) p/x 42          # 以十六进制打印
(gdb) p/t 42          # 以二进制打印
(gdb) p/d 0x2A        # 以十进制打印

格式控制符很实用。我调试底层驱动时,经常用p/x看寄存器值,用p/t看位域状态。你想想看,一个32位的状态寄存器,用十进制看根本看不出哪一位是1,用二进制一目了然。

小技巧:print还可以调用函数!比如p strlen(str),GDB会真的去执行这个函数。但要注意,这可能会改变程序状态,慎用。

2.6 退出调试:quit

quit,简写q。这个没什么好说的,退出GDB。如果程序还在运行,GDB会问你是否确认退出。

我个人的习惯是:调试结束后,先用info breakpoints看一眼还有哪些断点没删,然后quit。有时候第二天回来继续调,发现断点还在,省得重新设。

知识体系总览

下面这张图把这七个命令的关系梳理了一下,方便你记忆:

GDB七种基础命令关系图 GDB调试会话 run break next / step continue print quit 箭头方向表示命令的典型使用顺序

从这张图可以看出,调试的基本流程是:先用run启动程序,用break设好断点,然后用next/step单步跟踪,用continue跳过不关心的部分,用print查看变量值,最后用quit退出。这七个命令串起来,就是一个完整的调试循环。

好了,这一章的内容就这些。记住,调试不是背命令,而是理解程序的执行流。这七个命令你练熟了,后面多线程调试的那些高级技巧才能派上用场。

可可精品资料库