2、GDB基本命令:run/start、continue、next、step、finish、until、list、print、whatis、ptype

调试器这东西,说白了就是给程序装个「慢放键」。GDB 作为 Linux 下最经典的调试工具,它的基本命令就像你的手和脚——用熟了,根本不用过脑子。今天我就带你把这些命令捋一遍,都是我这些年踩坑踩出来的经验。

核心思想:调试不是瞎跑,而是有节奏地控制程序执行,并在关键点观察状态。

2.1 启动与运行:run vs start

先说说怎么让程序跑起来。你编译好一个带 -g 选项的可执行文件,然后 gdb ./a.out 进去,这时候程序还没动呢。

run 就是直接开跑。不带断点的话,程序一口气执行完。我个人习惯是先用 break main 设个断点,再用 run,这样能停在入口处慢慢看。

start 呢?它相当于 break main 加 run 的合体。你输入 start,GDB 会自动在 main 函数入口停住。嗯,这个命令我特别喜欢在快速验证时用,省得敲两行。

(gdb) start
Temporary breakpoint 1 at 0x4004d6: file test.c, line 5.
Starting program: /home/user/a.out

Temporary breakpoint 1, main () at test.c:5
5           int a = 10;

小技巧:如果程序需要命令行参数,用 run arg1 arg2 或者 set args arg1 arg2 提前设好。start 也支持传参,比如 start arg1。

2.2 继续执行:continue

程序停在断点上了,你想让它继续跑,直到下一个断点或结束。这时候用 continue(简写 c)。

我曾经在调试一个网络服务时,断点设在数据接收函数里。每次 continue 后,程序处理完一个包又停回来。配合 print 看变量变化,特别直观。

(gdb) c
Continuing.

Breakpoint 1, handle_packet (pkt=0x7fffffffdb10) at server.c:42
42          process(pkt);

2.3 单步执行:next vs step

这两个命令是调试的「显微镜」。区别就一句话:next 跳过函数,step 钻进函数。

  • next (n):执行当前行,如果遇到函数调用,把它当成一个整体执行完,不进去。
  • step (s):执行当前行,如果遇到函数调用,就跳进函数内部。

举个例子,假设当前停在 result = calc(a, b); 这一行:

(gdb) n   // 直接执行完 calc,停在下一行
(gdb) s   // 跳进 calc 函数内部

我个人习惯是:不确定函数有没有 bug 时用 step,确定没问题就用 next 跳过。你想想看,每次 step 进标准库函数里,那代码量够你看半天的。

注意:step 遇到没有调试信息的函数(比如系统库),会直接执行完。这时候可以用 stepi 按指令级别单步,但那是另一层话题了。

2.4 跳出与跑到指定位置:finish 和 until

finish 用来跳出当前函数。你 step 进一个函数,看了一半发现没问题,想直接回到调用处?finish 搞定。它会执行完当前函数,然后停在返回后的那一行。

(gdb) finish
Run till exit from #0  calc (a=10, b=20) at test.c:12
0x00000000004004f6 in main () at test.c:8
8           result = calc(a, b);

until 呢?它用来跑到指定行号。比如你在一个循环里,想直接跑到第 20 行:

(gdb) until 20

我记得有一次调试一个死循环,用 until 配合行号,快速跳过了前面几百次迭代,直接定位到出问题的那个分支。嗯,这命令在循环调试里特别好用。

2.5 查看源码:list

list(简写 l)用来显示源码。默认显示当前执行位置附近的 10 行。你可以指定行号、函数名,甚至文件加行号。

(gdb) list
5           int a = 10;
6           int b = 20;
7           int result;
8           result = calc(a, b);
9           return 0;
10      }

(gdb) list calc        // 显示 calc 函数的源码
(gdb) list test.c:1    // 显示 test.c 第 1 行开始

我个人习惯是:调试时保持源码窗口打开。如果你用 TUI 模式(gdb -tui),list 命令基本用不上,因为源码一直显示着。但纯命令行下,list 就是你的眼睛。

2.6 查看数据:print、whatis、ptype

调试的核心就是看数据。这三个命令帮你从不同角度观察变量。

print (p):打印变量的值。支持表达式,比如 p a+b、p &a、p *ptr。

(gdb) p a
$1 = 10
(gdb) p &a
$2 = (int *) 0x7fffffffdabc
(gdb) p a + b
$3 = 30

whatis:告诉你变量的类型。简单直接,适合快速确认。

(gdb) whatis a
type = int
(gdb) whatis ptr
type = int *

ptype:比 whatis 更详细。对于结构体、类等复杂类型,它会展开所有成员。

(gdb) ptype student
type = struct student {
    char name[32];
    int age;
    float score;
}

我曾经在调试一个内核模块时,遇到一个嵌套了 5 层的结构体。用 ptype 一层层看,比翻头文件快多了。说白了,ptype 就是你的「类型浏览器」。

2.7 知识体系总览

下面这张图帮你理清这些命令的关系:

GDB 基本命令体系 程序执行控制 ▶ run / start — 启动程序 ▶ continue — 继续执行到下一断点 ▶ next — 单步跳过函数 ▶ step — 单步进入函数 跳转与查看 ▶ finish — 跳出当前函数 ▶ until — 跑到指定行号 ▶ list — 查看源码 数据查看 ▶ print — 打印变量值/表达式 ▶ whatis — 查看变量类型 ▶ ptype — 查看详细类型结构 使用建议 • 先设断点,再用 run/start • 确定函数无 bug 用 next • 怀疑函数内部用 step • 循环调试多用 until

2.8 实战组合拳

光记命令没用,得会用。我给你一个典型的调试流程:

  1. gdb ./program 进入调试
  2. break main 在 main 设断点
  3. run 启动,停在 main
  4. list 看看附近代码
  5. next 一步步走,遇到可疑函数用 step 进去
  6. 在函数里用 print 看变量,ptype 看结构
  7. 确认没问题用 finish 跳出
  8. 想跳过循环用 until 行号
  9. 继续用 continue 跑到下一个断点

我的习惯:调试时右手放在小键盘区,左手放在方向键附近。next、step、continue 这些命令我都用简写:n、s、c。敲得快,思路不容易断。

这些命令看着简单,但组合起来威力很大。你想想看,一个程序从上亿条指令中跑出 bug,你能用这些命令精确控制每一步,这就是调试的乐趣所在。

嗯,今天就先聊到这儿。这些命令你先练熟,后面章节我们会用它们解决更复杂的问题。

可可精品资料库