x命令是gdb中唯一不依赖符号表、可直接查看任意内存地址的命令,其格式x/nfu中n为单元数量、f为显示格式(如x/d/s/i)、u为单元大小(b/h/w/g),三者必须明确指定以避免越界或误读。

x 命令是唯一可靠、直接、不依赖符号表的查看内存方式。其他命令(如 p)只能查变量,而 x 能查任意地址——哪怕那块内存没变量名、刚 malloc 出来、或已被释放。
用 x/nfu 看清地址、长度、格式三要素
x 的参数不是可选的“锦上添花”,而是必须明确的三个维度:多少个单元、怎么显示、每个单元多大。
- n 是数量(十进制整数),不是字节数;它乘以单位 u 才是总字节数
- f 是格式,x(十六进制)最常用,s(字符串)和 i(指令)有特殊语义,不能混用
- u 是单位,b(1 字节)、h(2 字节)、w(4 字节)、g(8 字节)必须匹配目标数据的真实对齐与大小
常见错误现象:
-
x/16xw &buf想看 16 字节,结果看了 64 字节(因为w=4× 16 = 64) -
x/s &ptr对一个未初始化指针执行,GDB 尝试从随机地址读字符串,可能卡住或报Cannot access memory -
x/8x &arr忘写单位,默认w,但arr是char[8],结果越界读了 32 字节
正确做法:
- 查 16 字节原始数据:用
x/16xb &buf - 查 4 个 int:用
x/4dw &arr(假设arr是int[4]) - 查字符串:先确认指针有效,再用
x/s ptr(不用&ptr,ptr本身已是地址)
按回车连续查看下一段内存时,地址自动递进
GDB 会记住上次x 命令的格式、单位和结束地址。按回车后,它从**上一条命令显示的最后一字节之后**开始,用相同 n、f、u 继续显示。
这个机制很实用,但也容易误判:
- 上次执行
x/8xb 0x7fffffffe000,显示了0x7fffffffe000~0x7fffffffe007 - 回车后实际执行的是
x/8xb 0x7fffffffe008,不是0x7fffffffe001 - 如果你本意是“逐字节滑动”,就得手动改地址,或用
x/1xb +1这类带偏移的写法
建议场景:
- 快速扫视栈帧:停在函数内,
x/16xb $rsp→ 回车 → 再回车,一屏一屏往下看 - 检查结构体布局:先
x/8xb &s,再回车看后续字段,比反复敲地址快得多
查数组、结构体、动态内存要小心对齐和边界
C/C++ 中数组名、结构体变量名、malloc 返回值都是地址,但它们的内存解释高度依赖类型。GDB 不做类型检查,只按你给的u 和 f 硬读。
典型陷阱:
-
char buf[10]; x/10xb buf安全;但x/10xw buf会读 40 字节,大概率越界到相邻变量甚至栈帧 -
struct { int a; char b; } s;——sizeof(s)通常是 8(因对齐),但x/8xb &s才能看清填充字节;用x/2dw &s会把b和 padding 当成第二个int解析,值完全错乱 -
int <em>p = malloc(3 </em> sizeof(int)); x/3dw p正确;但x/12xb p虽然字节数对,却丢失了整型语义,看不出哪 4 字节是一个int
更稳妥的做法:
- 先用
ptype看类型:ptype p确认是指针,ptype *p看指向类型 - 再按类型选单位:
int<em></em>→ 用w,short→ 用h,char*→ 用b - 配合
@操作符显式指定长度:x/5dw p等价于x/5dw p@20,但后者更直白
别依赖默认值,w 和 x 不是万能组合
很多人记成 “x/4x 就是看 4 个十六进制数”,但省略 u 时默认是 w(4 字节),x/4x &var 实际读 16 字节;省略 n 时默认是 1,x/xw &var 只读 4 字节。
最容易被忽略的点:
- 在 64 位程序里,指针是 8 字节,但
x/xw &ptr只读低 4 字节,高 4 字节被截断;要看完整指针值,必须用x/xg &ptr -
float和double必须用f格式 + 对应单位(f单位是w,d单位是g),用x格式读出来的是位模式,不是数值 -
x/i反汇编时,u必须匹配 CPU 架构(x86_64 用w或g,ARM64 通常用w),否则指令解码错乱
真正省事的方式是:每次用 x 前,心里默念一遍 “我要看几个?每个几字节?按什么意义解释?”——然后写全 nfu,不省那几个字母。











