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

说实话,很多初学者一上来就问我:「GDB到底怎么用?」

我的回答永远是:先把这七个命令练熟。这七个命令就像你手里的七把工具,搞定了它们,80%的调试场景你都能应付。

核心命令速查表

命令缩写作用
runr启动程序运行
breakb设置断点
nextn单步执行(不进入函数)
steps单步执行(进入函数)
continuec继续运行到下一个断点
printp打印变量值
quitq退出GDB

2.1 run —— 让程序跑起来

run命令,说白了就是告诉GDB:「开始执行吧!」

我个人习惯在启动GDB后,先检查一下代码有没有加载成功,然后再敲run。如果程序需要命令行参数,直接跟在后面就行:

(gdb) run arg1 arg2

嗯,这里有个小坑。我曾经在调试一个网络服务程序时,忘了加参数,结果程序直接报错退出。后来我养成了一个习惯:启动前先用show args看看参数对不对。

小技巧:如果程序已经跑起来了,想重新开始?再敲一次run就行,GDB会问你是否重启,回答y即可。

2.2 break —— 给程序「踩刹车」

breakpoint,断点。这是调试的灵魂。

你想想看,如果没有断点,程序一溜烟就跑完了,你根本来不及看中间发生了什么。break就是让程序在指定位置停下来,等你来检查。

设置断点有几种常用方式:

  • break main —— 在main函数入口停
  • break 42 —— 在第42行停
  • break func_name —— 在某个函数入口停
  • break file.c:100 —— 在指定文件的第100行停

我记得有一次,一个同事调试了整整一下午都没找到bug。我过去看了一眼,发现他断点打错了位置。其实他应该打在循环内部,结果打在了循环外面。这种低级错误,谁都会犯,但确实浪费时间。

注意:断点不要打太多。我曾经见过有人一口气打了20多个断点,结果程序每走几步就停一次,反而看不清逻辑了。一般3-5个关键断点就够了。

2.3 next 与 step —— 单步执行的两兄弟

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

  • next (n):下一行,不进入函数内部。如果当前行调用了函数,直接执行完整个函数,停在下一行。
  • step (s):下一行,会进入函数内部。如果当前行调用了函数,跳进函数里,停在函数的第一行。

什么时候用next?当你确定某个函数没问题,不想看它内部细节的时候。

什么时候用step?当你怀疑某个函数内部有bug,想进去一探究竟的时候。

我个人的经验是:先next几步,感觉不对劲了,再step进去看。别一上来就step,那样容易迷失在层层嵌套的函数调用里。

// 示例代码
#include <stdio.h>

int add(int a, int b) {
    return a + b;  // 如果在这里用step,会进入add函数内部
}

int main() {
    int x = 10;
    int y = 20;
    int z = add(x, y);  // 用next会直接得到结果,用step会进入add
    printf("z = %d\n", z);
    return 0;
}

2.4 continue —— 快速跳到下一个断点

continue,缩写c。它的意思是:「别停,继续跑,直到遇到下一个断点。」

这个命令在循环调试中特别有用。比如你在循环里打了个断点,想看看第100次循环时的状态,难道要next 100次?当然不。直接continue,每次循环停一次,你数着次数就行。

我曾经调试过一个死循环问题,就是在循环体里打了断点,然后用continue一次次观察变量变化,最终发现是循环条件写错了。嗯,这种问题用continue排查效率很高。

2.5 print —— 查看变量的「眼睛」

print,缩写p。它的作用就是打印变量的当前值。

基本用法:

(gdb) print x
$1 = 10
(gdb) print &x
$2 = (int *) 0x7fffffffe4a4
(gdb) print array[3]
$3 = 42

你还可以用print做简单的计算:

(gdb) print x + y
$4 = 30

这里有个我踩过的坑。有一次我打印一个指针变量,看到地址是0x0,我还以为程序没问题。后来才发现,那个指针已经被free掉了,但没有置为NULL。所以打印出来的地址虽然是合法的,但指向的内存已经不可用了。这就是经典的「悬空指针」问题。

建议:调试指针时,不光要看指针的值,还要看它指向的内容。用print *ptr来解引用查看。

2.6 quit —— 优雅地退出

quit,缩写q。调试完了,输入q回车,就退出了。

如果你在调试过程中想退出,GDB会问你是否确认。直接y就行。

嗯,这里有个小细节:如果你用Ctrl+C中断了正在运行的程序,然后想退出,记得先输入quit,别直接关终端。否则可能会留下一些后台进程。

知识体系总览

下面这张图,是我自己总结的GDB基本命令使用流程。你可以把它当作调试时的「导航图」:

GDB基本命令调试流程 启动GDB break 设置断点 run 启动程序 是否进入函数? 是 step 进入函数 否 next 单步执行 print 查看变量

这张图展示了我平时调试的基本流程:启动GDB → 设置断点 → 运行程序 → 判断是否需要进入函数 → 用step或next单步执行 → 用print查看变量。循环往复,直到找到问题。

实战小例子

光说不练假把式。我们拿一个简单的例子走一遍流程:

// bug.c
#include <stdio.h>

int main() {
    int sum = 0;
    for (int i = 1; i <= 10; i++) {
        sum += i;
    }
    printf("1到10的和是:%d\n", sum);  // 预期输出55
    return 0;
}

假设我们怀疑sum计算不对,可以这样调试:

$ gcc -g -o bug bug.c
$ gdb ./bug
(gdb) break 6          # 在循环体内部打断点
(gdb) run
(gdb) print i          # 查看当前i的值
$1 = 1
(gdb) print sum        # 查看当前sum的值
$2 = 0
(gdb) next             # 执行一次循环
(gdb) print sum
$3 = 1
(gdb) continue         # 继续到下一次循环
(gdb) print i
$4 = 2
(gdb) print sum
$5 = 3
(gdb) quit

你看,通过这几个命令的组合,我们就能清楚地看到每次循环中变量的变化情况。这就是GDB调试的基本功。

核心要点回顾:

  • run让程序跑起来,break让程序停下来
  • next跳过函数,step钻进函数
  • continue快速跳到下一个断点
  • print是你看变量的眼睛
  • quit优雅退出

这七个命令,说白了就是调试的「七种武器」。刚开始用的时候可能会手忙脚乱,但多练几次,形成肌肉记忆就好了。我当年也是从这几个命令开始,一步步走到今天的。

我的建议:找一段自己以前写的代码,故意制造几个bug,然后用这七个命令去排查。实践是最好的老师。

可可精品资料库