第1章:变量与内存查看——print命令进阶、display命令、x命令查看内存、ptype命令查看类型

调试C/C++程序,说白了就是跟变量和内存打交道。我见过太多新手,一上来就只会用print打印个整型变量,遇到指针、结构体就懵了。今天咱们就把GDB里这几个查看命令彻底搞明白。

1.1 print命令:不只是打印值

print(简写p)是GDB里最常用的命令。但你真的会用吗?

1.1.1 基本用法

(gdb) p i
$1 = 42

看到那个$1了吗?这是GDB自动给结果编号。后面你可以直接用$1引用这个值。

小技巧: 我习惯用p/x看十六进制,p/d看十进制,p/c看字符。比如检查指针地址时,十六进制最直观。

1.1.2 格式化输出

格式 说明 示例
x 十六进制 p/x ptr
d 有符号十进制 p/d val
u 无符号十进制 p/u val
c 字符 p/c ch
f 浮点数 p/f fval
s 字符串 p/s str
注意: 我曾经在调试一个网络协议栈时,用p/x看了一个指针,结果发现地址是0xdeadbeef——典型的野指针。所以看到这种特殊地址,心里要有数。

1.1.3 打印复杂表达式

print不仅能打变量,还能算表达式:

(gdb) p a + b
$2 = 100
(gdb) p *ptr
$3 = 42
(gdb) p array[3]
$4 = 7

嗯,这里要注意:p *ptr解引用指针时,如果指针非法,GDB会报错。我建议先p ptr确认地址是否合理。

1.2 display命令:自动刷新你的观察窗口

调试循环时,每次单步都要手动print,烦不烦?display就是干这个的。

1.2.1 基本用法

(gdb) display i
1: i = 0
(gdb) display /x ptr
2: /x ptr = 0x7fffffffe4a0

每次程序停下来,GDB自动打印这些表达式。我个人习惯在调试链表时,把display *node和display node->next都加上。

1.2.2 管理display列表

命令 作用
info display 列出所有display项
undisplay N 删除编号N的display
disable display N 临时禁用,不删除
enable display N 重新启用
经验之谈: 我调试一个内存池时,同时display了6个变量。后来发现太多了,眼花。建议最多同时display 3-4个,关键的留着,不关键的用print临时看。

1.3 x命令:直接查看内存

x命令是GDB里最强大的内存查看工具。它的格式是:x/[数量][格式][单位] 地址

1.3.1 单位说明

单位 含义 大小
b 字节 1字节
h 半字 2字节
w 字 4字节
g 双字 8字节

1.3.2 实战示例

// 查看数组前16个字节,以十六进制显示
(gdb) x/16xb buffer
0x7fffffffe4a0: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
0x7fffffffe4a8: 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10

// 以4字节字为单位,查看10个字
(gdb) x/10xw buffer
0x7fffffffe4a0: 0x04030201 0x08070605 0x0c0b0a09 0x100f0e0d
避坑指南: 我曾经在调试一个嵌入式程序时,用x/100xb查看一个只有20字节的缓冲区。结果GDB直接访问了非法地址,导致目标板挂了。记住:先确认内存范围,再查看。

1.3.3 查看字符串

(gdb) x/s 0x4005a0
0x4005a0: "Hello, World!"

这个在分析缓冲区溢出时特别有用。你可以直接看内存里有没有被覆盖的字符串。

1.4 ptype命令:揭开类型的面纱

遇到复杂类型,尤其是结构体、联合体、函数指针时,ptype是你的救星。

1.4.1 基本用法

(gdb) ptype node
type = struct Node {
    int data;
    struct Node *next;
}

你看,GDB把结构体的完整定义都列出来了。包括成员类型、偏移量(如果有的话)。

1.4.2 查看typedef

(gdb) ptype size_t
type = unsigned long

有时候代码里一堆typedef,看得眼花。用ptype直接看原始类型,心里就有底了。

1.4.3 查看函数指针

(gdb) ptype callback
type = int (*)(void *, int)

嗯,这个在调试回调函数时特别有用。我经常用它确认函数指针的签名是否正确。

我的习惯: 遇到不熟悉的第三方库结构体,先ptype看定义,再p *ptr看内容。两步走,基本不会出错。

1.5 知识体系总览

下面这张图总结了本章的核心内容,你可以把它当作调试时的速查表:

GDB变量与内存查看命令体系 GDB查看命令 print 命令 格式化输出 表达式计算 display 命令 自动刷新 列表管理 x 命令 内存查看 格式控制 ptype 命令 类型查看 结构体展开

1.6 实战组合拳

最后,分享一个我调试时的常用套路:

  1. 遇到崩溃,先bt看调用栈
  2. 找到可疑变量,ptype确认类型
  3. p打印值,x查看内存布局
  4. 循环中加上display自动跟踪

这套组合拳,我用了十年,基本能解决90%的内存问题。你试试看,绝对比瞎猜有效率。

核心要点:
  • print看值,x看内存,ptype看类型,display自动跟踪
  • 格式化输出用/x、/d、/s等
  • x命令的单位b/h/w/g要记牢
  • ptype是理解复杂类型的利器

可可精品资料库