2. GDB基本命令:run、break、next、step、continue、print、quit
说实话,很多初学者一上来就问我:「GDB到底怎么用?」
我的回答永远是:先把这七个命令练熟。这七个命令就像你手里的七把工具,搞定了它们,80%的调试场景你都能应付。
核心命令速查表
| 命令 | 缩写 | 作用 |
|---|---|---|
| run | r | 启动程序运行 |
| break | b | 设置断点 |
| next | n | 单步执行(不进入函数) |
| step | s | 单步执行(进入函数) |
| continue | c | 继续运行到下一个断点 |
| p | 打印变量值 | |
| quit | q | 退出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 → 设置断点 → 运行程序 → 判断是否需要进入函数 → 用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,然后用这七个命令去排查。实践是最好的老师。
可可精品资料库