4. GDB数据查看
调试程序,说白了就是跟数据打交道。你写的代码再漂亮,变量值不对,一切都是白搭。这一章,我带你彻底搞懂GDB里那些查看数据的命令。嗯,都是我在项目里摸爬滚打总结出来的实用技巧。
4.1 print命令:最基础的查看方式
print(简写p)是GDB里用得最多的命令。它的作用很简单:打印表达式的值。
(gdb) p my_var
$1 = 42
(gdb) p my_array[3]
$2 = 7
(gdb) p &my_var
$3 = (int *) 0x7fffffffe4a0
看到那个$1、$2了吗?这是GDB自动给每个打印结果分配的编号。你可以直接用$1来引用之前的结果,省得再打一遍。
p /x查看十六进制,用p /d看十进制,用p /c看字符。比如p /x my_var,一眼就能看出内存里的原始值。
4.2 格式化输出:让数据更顺眼
GDB支持多种输出格式,我整理了一张表,你一看就明白:
| 格式 | 说明 | 示例 |
|---|---|---|
| /x | 十六进制 | p /x 255 → 0xff |
| /d | 有符号十进制 | p /d 0xff → 255 |
| /u | 无符号十进制 | p /u -1 → 4294967295 |
| /o | 八进制 | p /o 64 → 0100 |
| /t | 二进制 | p /t 5 → 101 |
| /c | 字符 | p /c 65 → 'A' |
| /f | 浮点数 | p /f 3 → 3.0 |
| /s | 字符串 | p /s buf |
我在调试网络协议栈时,经常用/x看报文头部的十六进制内容。有一次发现一个校验和错误,就是靠p /x checksum一眼看出高位和低位写反了。
4.3 display命令:自动跟踪变量
单步调试时,每次都要手动p一下变量,太烦了。这时候display就派上用场了。它会在每次程序暂停时,自动打印你指定的变量。
(gdb) display my_var
1: my_var = 42
(gdb) display /x my_ptr
2: /x my_ptr = 0x7fffffffe4a0
(gdb) display my_array[0]@5
3: my_array[0]@5 = {1, 2, 3, 4, 5}
看到没?每次执行next或step后,这些变量会自动更新。用info display可以查看所有自动显示的变量,用undisplay 编号可以取消某个显示。
4.4 whatis和ptype:搞清楚变量的真面目
有时候你看到一个变量,但不确定它的类型。这时候whatis和ptype就派上用场了。
(gdb) whatis my_var
type = int
(gdb) whatis my_func
type = int (int, char *)
(gdb) ptype my_struct
type = struct my_struct {
int id;
char name[32];
float value;
}
whatis给出简洁的类型信息,ptype则会把结构体、联合体、枚举的完整定义打印出来。我在调试一个大型项目时,经常用ptype查看某个结构体里到底有哪些字段,省得来回翻代码。
4.5 x命令:直接查看内存
x命令(examine的缩写)是GDB里最强大的命令之一。它让你直接查看内存里的原始数据,格式是:x /[数量][格式][大小] 地址。
(gdb) x/10xb 0x7fffffffe4a0
0x7fffffffe4a0: 0x2a 0x00 0x00 0x00 0x01 0x00 0x00 0x00
0x7fffffffe4a8: 0x03 0x00
(gdb) x/5wd 0x7fffffffe4a0
0x7fffffffe4a0: 42 1 3 0 0
(gdb) x/3s 0x7fffffffe4a0
0x7fffffffe4a0: "hello"
0x7fffffffe4a4: "world"
0x7fffffffe4a8: "!"
大小选项说明:
b:1字节(byte)h:2字节(halfword)w:4字节(word)g:8字节(giant)
x/100xb 变量地址来查看变量周围的内存区域。有一次发现数组后面的几个字节被改写了,顺着这个线索找到了一个缓冲区溢出的bug。
4.6 知识体系总览
下面这张图,是我梳理的GDB数据查看命令的核心逻辑。你看一眼,心里就有数了:
4.7 综合实战:一个完整的调试场景
假设你有一个结构体数组,里面存着学生的成绩。你想看看某个学生的数据对不对:
(gdb) p students[3]
$1 = {id = 1004, name = "张三", scores = {85, 92, 78}}
(gdb) ptype students[3]
type = struct student {
int id;
char name[20];
int scores[3];
}
(gdb) display students[3].scores[0]@3
1: students[3].scores[0]@3 = {85, 92, 78}
(gdb) x/3wd &students[3].scores
0x7fffffffe4c0: 85 92 78
你看,先用p看整体,再用ptype确认结构,然后用display自动跟踪,最后用x确认内存布局。这一套组合拳打下来,数据有没有问题,一目了然。
ptype把结构体定义打印出来,对照着看每个字段的值。这样不容易漏掉某个成员。
好了,这一章的内容就这些。记住,数据查看是调试的基础,把这些命令练熟了,以后遇到再复杂的bug,你也能从容应对。
可可精品资料库