2. GDB基本命令

好,咱们正式开始动手。这一章我带你过一遍GDB最核心的几个命令。说白了,你只要把这几个命令玩熟了,日常调试中80%的问题都能搞定。我当年刚入行时,就靠这几招在项目里抓了不少bug。

2.1 启动GDB:两种方式,你都得会

启动GDB其实就两种路子。第一种最直接:

gdb ./my_program

这样GDB会加载你的程序,但不会立刻跑起来。它等着你下命令呢。

第二种方式,是程序已经跑起来了,突然卡死或者出异常。这时候你可以:

gdb ./my_program core_dump_file

或者,如果你想让GDB挂到一个正在运行的进程上:

gdb -p <PID>

嗯,这里要注意:core dump文件不是默认生成的。你需要先执行ulimit -c unlimited。我曾经在一个服务器项目里,程序半夜崩溃了,结果发现core dump没开,那叫一个抓狂。从那以后,我每个项目的第一件事就是检查core dump配置。

我的习惯: 调试前先确认编译时加了-g选项。否则你看到的全是汇编,那画面太美我不敢看。

2.2 设置断点:让程序停在你想停的地方

断点是调试的灵魂。没有断点,你只能眼睁睁看着程序跑完,啥也抓不到。

最基本的用法:

(gdb) break main          # 在main函数入口停住
(gdb) break 42             # 在当前文件的第42行停住
(gdb) break file.c:100     # 在file.c的第100行停住

你还可以加条件。比如我只想在第42行停住,但前提是i == 5:

(gdb) break 42 if i == 5

这个功能太实用了。我在调试一个循环时,经常遇到前100次都没问题,第101次就崩了。没有条件断点,你得手动按100次continue,手都按酸了。

核心要点: 断点可以加在函数名、行号、文件名+行号上。条件断点能帮你精准定位。

查看你设了哪些断点:

(gdb) info breakpoints

删除断点:

(gdb) delete 1   # 删除编号为1的断点
(gdb) delete     # 删除所有断点

2.3 运行程序:run和start的区别

设好断点后,就该让程序跑起来了。最常用的命令是run:

(gdb) run
(gdb) run arg1 arg2   # 带命令行参数运行
(gdb) run < input.txt # 重定向输入

还有一个命令叫start。它相当于break main + run。也就是说,它会在main函数的第一行停住。我个人习惯用start,省得再敲一遍断点命令。

注意: 如果程序已经跑起来了,你想重新跑一遍,直接敲run就行。GDB会问你是否重启,回答y即可。

2.4 单步执行:next和step,别搞混了

这两个命令是调试时用得最频繁的。很多人一开始分不清,其实很简单:

命令 含义 会不会进入函数内部
next(或n) 执行下一行 不会
step(或s) 执行下一行 会

举个例子。假设你有这么一段代码:

int a = foo();   // 第10行
int b = bar();   // 第11行

如果当前在第10行,你敲next,GDB会直接执行完foo(),然后停在第11行。你根本看不到foo()内部发生了什么。

如果你敲step,GDB会跳进foo()函数内部,让你一行一行地看它怎么执行的。

我刚开始学的时候,经常在调试库函数时不小心step进去了,结果看到一堆汇编代码,吓得赶紧按finish跳出来。后来学乖了,不确定的函数先用next跳过。

小技巧: 如果你不小心step进了一个函数,可以用finish命令直接执行完当前函数,然后回到调用处。

2.5 查看变量:print和它的变体

调试的核心就是看变量值。最基础的命令是print:

(gdb) print i           # 打印变量i的值
(gdb) print &i          # 打印变量i的地址
(gdb) print *ptr        # 打印指针指向的值
(gdb) print array[5]    # 打印数组第6个元素

你还可以打印表达式:

(gdb) print i + j
(gdb) print sizeof(arr)

有时候你想一直盯着某个变量看,每次手动敲print太麻烦。这时候可以用display:

(gdb) display i         # 每次程序停住时,自动打印i的值
(gdb) info display      # 查看所有自动显示的变量
(gdb) undisplay 1       # 取消编号为1的自动显示

还有一个命令叫whatis,可以查看变量的类型:

(gdb) whatis i
type = int

嗯,这个命令在调试复杂结构体时特别有用。有时候你看到一个变量名,但不确定它是什么类型,whatis一下就知道。

总结一下: print看值,display自动看值,whatis看类型。这三个命令配合使用,基本能搞定所有变量查看需求。

2.6 本章知识体系

下面这张图帮你梳理了本章的核心命令和它们之间的关系:

GDB基本命令知识体系 启动GDB gdb ./prog / gdb -p PID 设置断点 break / info break / delete 运行程序 run / start 单步执行 next(不进入)/ step(进入) 查看变量 print / display / whatis 调试流程总结 启动GDB → 设置断点 → 运行程序 → 单步执行 → 查看变量 发现问题后,修改代码,重新编译,再来一轮

这张图把本章的核心命令串起来了。你从启动GDB开始,设置断点,然后运行程序。程序在断点处停住后,你用next或step单步执行,同时用print查看变量值。发现问题后,修改代码,重新编译,再来一轮。这就是调试的基本循环。

好了,这一章的内容就这些。你先把这几个命令练熟,后面我们会在这个基础上,讲更多高级技巧。