第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 编号可以取消某个显示。

注意: display多了会影响调试速度。我曾经一次display了20多个变量,结果单步执行慢得像蜗牛。建议只display你最关心的3-5个变量。

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数据查看命令的核心逻辑。你看一眼,心里就有数了:

GDB数据查看命令体系 数据查看 print (p) 打印表达式值 支持格式化输出 结果自动编号 display 自动跟踪变量 每次暂停时更新 info/undisplay管理 whatis/ptype 查看变量类型 ptype显示完整定义 适合复杂结构体 x (examine) 直接查看内存 格式:x/[数量][格式][大小] 支持b/h/w/g大小单位 核心思路:从变量 → 类型 → 内存,层层深入

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,你也能从容应对。

可可精品资料库