2、GDB基本命令:启动程序、设置断点、运行控制、查看变量、单步执行
说实话,很多初学者一上来就想着用GDB搞多线程、搞core dump分析,结果连最基本的启动和暂停都搞不明白。我当年也是这样,拿着gdb ./a.out敲下去,然后盯着(gdb)提示符发呆——接下来该干嘛?
别急,这一章咱们就把最核心的五个操作吃透。你想想看,调试的本质是什么?说白了就是:让程序停下来,看看它里面发生了什么。这五个命令就是干这个的。
核心思想:调试不是乱试,而是有节奏地控制程序执行,在关键位置停下来检查状态。
2.1 启动程序:run 与 start
启动程序有两种方式,我习惯根据场景来选。
| 命令 | 作用 | 适用场景 |
|---|---|---|
run(或 r) |
直接运行程序,直到遇到断点或结束 | 已经设置好断点,想快速跑到第一个断点处 |
start |
在 main 函数入口处暂停 | 想从程序入口一步步看初始化逻辑 |
举个例子,假设你有一个 test.c:
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int c = a + b;
printf("c = %d\n", c);
return 0;
}
编译时记得加 -g 选项:
gcc -g -o test test.c
然后启动GDB:
gdb ./test
在 (gdb) 提示符下,如果你敲 start,程序会在 main 函数的第一行停下来。如果你敲 run,它会直接跑完——除非你提前设了断点。
我的习惯:刚开始调试时,我一般先用 start,从入口处一步步跟。等熟悉了代码流程,再用 run 配合断点快速定位。
2.2 设置断点:break 的三种常用姿势
断点就是让程序停下来的「路障」。GDB里设置断点用 break(简写 b)。我总结了三种最常用的写法:
- 按行号设置:
break 10—— 在源文件的第10行停下 - 按函数名设置:
break main—— 进入 main 函数时停下 - 按条件设置:
break 10 if a == 5—— 只有变量 a 等于 5 时才停下
我在项目中遇到过一个问题:一个循环跑了十万次,我想在某个特定条件下停下来。如果手动按 c(继续)十万次,手都得废掉。这时候条件断点就派上用场了:
(gdb) break 15 if i == 99999
嗯,这个技巧帮我省了不少时间。
注意:条件断点中的表达式必须是 C/C++ 合法的。比如 if a = 5 是赋值,不是比较,GDB不会报错但逻辑是错的。我曾经在这上面栽过跟头,排查了半天才发现是少写了一个等号。
2.3 运行控制:continue 与 中断
程序停下来了,怎么让它继续跑?用 continue(简写 c)。
但有时候你跑着跑着发现不对劲,想手动中断——按 Ctrl + C 就行。GDB会捕获这个信号,把程序停在当前执行的位置。
我记得有一次调试一个死循环,程序跑起来就卡死了。我按了 Ctrl + C,GDB停在了循环体里,我一看——哦,循环变量忘了递增。这种问题用肉眼扫代码很难发现,但一跑起来就原形毕露。
2.4 查看变量:print 与 display
程序停下来了,接下来就是「看看里面有什么」。最常用的命令是 print(简写 p):
(gdb) print a
$1 = 10
(gdb) print &a
$2 = (int *) 0x7fffffffe4a4
(gdb) print sizeof(a)
$3 = 4
print 可以打印变量值、地址、甚至表达式结果。比如 print a + b 会直接算出结果。
但如果你每次单步后都想看某个变量,每次都敲 print 就太累了。这时候用 display:
(gdb) display a
1: a = 10
之后每次程序停下来,GDB都会自动显示 a 的值。不想看了就用 undisplay 取消。
小技巧:用 info locals 可以一次性查看当前函数的所有局部变量。我调试复杂函数时经常先用这个命令「扫一眼」全局状态。
2.5 单步执行:step 与 next
单步执行是调试的「显微镜」。两个命令的区别很关键:
| 命令 | 简写 | 行为 |
|---|---|---|
step |
s |
进入函数内部 |
next |
n |
不进入函数,直接执行完当前行 |
举个例子,假设当前停在 printf("c = %d\n", c); 这一行:
- 敲
next:直接执行完printf,停在下一行 - 敲
step:会进入printf函数内部(如果你有它的源码)
我个人习惯是:确认函数没问题就用 next,怀疑函数有 bug 就用 step 进去看。别傻乎乎地每个函数都 step 进去,标准库函数里面几千行代码,你进去干嘛?
避坑指南:我曾经在调试一个第三方库时,习惯性地用了 step,结果一头扎进了汇编代码里。当时我连寄存器都看不懂,差点崩溃。后来学乖了——不确定的函数先用 next 跳过,真有必要再 step。
知识体系总览
下面这张图把本章的核心逻辑串起来了。你可以把它当作调试时的「操作地图」:
这张图展示的就是调试时的基本节奏:启动 → 设断点 → 运行 → 查看 → 单步。循环往复,直到找到问题。
好了,这一章的内容就到这里。五个命令,不多,但每一个都是调试的基石。你可以在自己的代码上试试,从 start 开始,用 next 一步步走,用 print 看看变量变化——相信我,这种感觉比盯着代码猜要踏实得多。