第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 知识体系总览
下面这张图总结了本章的核心内容,你可以把它当作调试时的速查表:
1.6 实战组合拳
最后,分享一个我调试时的常用套路:
- 遇到崩溃,先
bt看调用栈 - 找到可疑变量,
ptype确认类型 p打印值,x查看内存布局- 循环中加上
display自动跟踪
这套组合拳,我用了十年,基本能解决90%的内存问题。你试试看,绝对比瞎猜有效率。
核心要点:
- print看值,x看内存,ptype看类型,display自动跟踪
- 格式化输出用
/x、/d、/s等 - x命令的单位b/h/w/g要记牢
- ptype是理解复杂类型的利器
可可精品资料库