3、GDB基本命令:设置断点(break)、运行(run)、单步(step/next)、继续(continue)、打印(print)
好,咱们直接进入正题。调试器这东西,说白了就是让你能「看」着代码跑。GDB 的核心命令其实不多,但每一个都值得你好好琢磨。我个人习惯把这五个命令称为「调试五虎将」—— 断点、运行、单步、继续、打印。掌握了它们,你就掌握了 GDB 的命脉。
3.1 设置断点:让程序在你想停的地方停下来
断点是调试的起点。没有断点,程序就像脱缰的野马,你根本抓不住它。
最基本的用法是 break 行号。比如:
(gdb) break 42
Breakpoint 1 at 0x4005a0: file main.c, line 42.
这会在第 42 行设置一个断点。程序跑到这一行就会停下来,等你发号施令。
你也可以按函数名来设断点:
(gdb) break main
Breakpoint 2 at 0x400530: file main.c, line 12.
这会在 main 函数的入口处停下。我在项目中经常用这个,尤其是接手别人的代码时,先看看入口逻辑对不对。
break 42 if i == 5,这样只有 i 等于 5 时才会触发断点。省得你一次次手动跳过。
还有一种情况,你可能会遇到:断点设在了循环里,每次循环都停一次,烦不烦?嗯,这时候条件断点就派上用场了。
3.2 运行:让程序动起来
断点设好了,接下来就是让程序跑起来。命令很简单:run。
(gdb) run
Starting program: /home/user/myapp
Breakpoint 1, main () at main.c:12
12 int x = 0;
程序会一直执行,直到遇到第一个断点。如果没有断点,它就会跑完全程。
你还可以给 run 传参数,就像在命令行里一样:
(gdb) run arg1 arg2 arg3
或者重定向输入输出:
(gdb) run < input.txt > output.txt
我记得有一次调试一个网络服务程序,需要模拟不同的输入场景。用 run < test_case.txt 这种方式,省了我不少事。
run 会询问你是否重新启动。输入 y 确认即可。我曾经不小心按了 n,结果程序卡在那不动了,还以为 GDB 坏了。
3.3 单步:一步一步看代码怎么走
单步执行是调试的精髓。GDB 提供了两个命令:step 和 next。
next 是「下一步」,它会执行当前行,然后停在下一行。如果当前行是一个函数调用,next 会直接执行完整个函数,不会进入函数内部。
(gdb) next
13 int y = foo(x); // 执行完这一行,直接到下一行
step 是「步入」,它会进入函数内部,一行一行地执行。
(gdb) step
foo (a=0) at foo.c:5
5 return a + 1;
你想想看,什么时候用 next,什么时候用 step?
我的经验是:如果你怀疑某个函数内部有问题,就用 step 进去看看。如果你只是想跳过这个函数,用 next 就对了。说白了,step 是「深挖」,next 是「跳过」。
step 进入了一个标准库函数,结果在里面绕了半天出不来。后来学乖了,不确定的函数先用 next 跳过,真有需要再 step。
3.4 继续:让程序飞一会儿
单步执行虽然精细,但有时候你只想让程序跑到下一个断点。这时候就用 continue(简写 c)。
(gdb) continue
Continuing.
Breakpoint 2, main () at main.c:20
20 printf("x = %d\n", x);
程序会继续执行,直到遇到下一个断点或者程序结束。
你还可以指定跳过多少个断点:
(gdb) continue 3
这会跳过接下来的 3 个断点,在第 4 个断点处停下。嗯,这个用法我很少用,但在某些循环调试场景下确实能省点事。
3.5 打印:看看变量到底值多少钱
调试的核心就是看变量值。GDB 的 print 命令(简写 p)就是干这个的。
(gdb) print x
$1 = 42
你可以打印任何表达式:
(gdb) print x + y
$2 = 100
(gdb) print &x
$3 = (int *) 0x7fffffffe4a0
甚至打印结构体:
(gdb) print my_struct
$4 = {name = "hello", value = 10, next = 0x0}
如果结构体很大,可以用 print /x 以十六进制显示,或者 print /t 以二进制显示。
display 命令可以让 GDB 每次停下来时自动打印某个变量。比如 display x,这样你就不用每次都手动输入 print x 了。我在调试循环时特别喜欢用这个。
3.6 知识体系总览
下面这张图总结了这五个命令的关系和用法。你可以把它当作一个快速参考。
3.7 实战组合拳
光知道单个命令还不够,你得学会把它们组合起来用。我举个例子:
(gdb) break main # 在 main 函数入口设断点
(gdb) run # 启动程序,停在 main 入口
(gdb) next # 跳过初始化代码
(gdb) step # 进入可疑函数
(gdb) print buffer # 看看 buffer 内容
(gdb) continue # 继续跑,到下一个断点
(gdb) print result # 看看最终结果
这套组合拳我用了十年,几乎每天都会用到。你想想看,是不是很顺手?
break设断点,run启动,step/next单步,continue继续,print看值- 条件断点用
break 行号 if 条件 step进函数,next跳过函数display可以自动打印变量,省得每次手动输入
好了,这五个命令就讲到这里。记住,调试不是一蹴而就的事,多练练自然就熟了。下次遇到 bug,别急着改代码,先想想能不能用 GDB 把它揪出来。