第十章(Examining Data)重难点详解
第十章的核心是**“程序数据的检查与分析”——通过gdb命令查看变量、表达式、内存、类型信息,解决“数据值是否正确”“内存布局是否合理”“类型定义是否匹配”等调试问题。本章重难点围绕表达式求值规则、数据输出格式控制、内存底层查看、类型信息解析、打印格式自定义、高级数据(数组/结构体)处理**展开,以下结合文档核心段落(标注🔶),按“基础数据查看→进阶格式控制→底层内存分析→高级类型与打印”的逻辑,一步一步拆解。
一、第一步:基础认知——第十章核心范畴与前置准备
1.1 第十章核心目标
第十章的核心是让gdb“读懂程序数据”,支撑调试中对数据正确性的验证,关键能力包括:
- 求值并打印表达式(
print/p); - 控制数据输出格式(如十六进制、十进制、字符串);
- 查看内存原始数据(
examine/x); - 解析变量与类型信息(
whatis/ptype); - 自定义打印格式(
set print系列命令); - 处理复杂数据(数组、字符串、结构体、指针)。
1.2 前置准备:示例程序与编译
以data_demo.c为例(含基本类型、数组、结构体、指针,覆盖第十章所有数据场景):
#include <stdio.h>
// 结构体类型
typedef struct {
int id; // 整型
char name[20]; // 字符串(数组)
float score; // 浮点型
} Student;
int main() {
int num = 1024; // 基本整型
char str[] = "gdb_data_demo"; // 字符数组(字符串)
float pi = 3.14159f; // 浮点型
Student stu = {101, "Alice", 95.5f}; // 结构体
int *p_num = # // 指针
// 故意留空,便于调试时查看数据
printf("num=%d, pi=%.3f\n", num, pi);
return 0;
}
编译(必须带调试信息-g,否则gdb无法关联变量与类型):
gcc -g -O0 -o data_demo data_demo.c
二、第二步:核心重难点1——表达式求值与基础打印(print命令)
print(缩写p) 是第十章最基础的命令,用于求值并打印表达式,支持任意合法的源码语言表达式(如变量、算术运算、指针解引用),是数据检查的“入口命令”(🔶1-2518至🔶1-2521)。
2.1 基础用法:打印变量与简单表达式
实操1:打印单个变量
gdb ./data_demo
# 启动后在main函数设断点,确保变量已初始化
(gdb) break main
Breakpoint 1 at 0x40054d: file data_demo.c, line 12.
(gdb) run
Starting program: ./data_demo
Breakpoint 1, main () at data_demo.c:12
12 int num = 1024; // 基本整型
# 执行到变量初始化后(单步或设断点到第18行)
(gdb) step 6 # 执行到第18行(printf前)
18 printf("num=%d, pi=%.3f\n", num, pi);
# 打印基本类型变量
(gdb) print num # 打印int变量
$1 = 1024
(gdb) print pi # 打印float变量
$2 = 3.1415899991989136
(gdb) print stu.id # 打印结构体成员
$3 = 101
(gdb) print *p_num # 打印指针解引用
$4 = 1024
文档依据(🔶1-2518):print默认按变量类型的“自然格式”打印(int十进制、float小数形式、结构体展开成员)。
实操2:打印复杂表达式
支持算术运算、逻辑判断、函数调用(需函数可安全调用)等表达式:
# 算术运算:num的平方
(gdb) print num * num
$5 = 1048576
# 逻辑判断:pi是否大于3
(gdb) print pi > 3
$6 = 1 # gdb中1表示true,0表示false
# 结构体成员运算:stu.score+2.5
(gdb) print stu.score + 2.5
$7 = 98.00000762939453
# 字符串长度(调用strlen函数,需包含<string.h>)
(gdb) print strlen(str)
$8 = 11 # "gdb_data_demo"长度为11
注意(🔶1-2519):表达式需符合当前源码语言的语法(如C语言的*p、C++的obj->mem),gdb会自动适配当前语言环境。
2.2 关键细节:print的选项与格式控制
print支持通过/f指定输出格式(f为格式符),也支持通过选项覆盖全局打印设置(🔶1-2521至🔶1-2531),核心格式与选项如下:
1. 基础输出格式(print /f expr)
常用格式符(🔶1-2520):
| 格式符 | 含义 | 适用场景 |
|---|---|---|
x | 十六进制 | 内存地址、二进制数据 |
d | 十进制(默认) | 整型变量 |
u | 无符号十进制 | 无符号整型 |
o | 八进制 | 硬件寄存器、权限位 |
t | 二进制 | 位运算结果、标志位 |
a | 地址格式(带符号) | 指针(显示对应的函数/源码) |
c | 字符 | 字符变量、字符串单个字符 |
s | 字符串 | 字符数组、char*指针 |
f | 浮点型(默认) | float/double变量 |
实操:格式符的使用
# 1. 十六进制打印num(默认十进制1024→0x400)
(gdb) print /x num
$9 = 0x400
# 2. 二进制打印num(1024的二进制)
(gdb) print /t num
$10 = 10000000000
# 3. 地址格式打印指针p_num(显示指向的变量)
(gdb) print /a p_num
$11 = 0x7fffffffde74 <num> # 明确指向num变量
# 4. 字符格式打印str的第3个字符(索引2)
(gdb) print /c str[2]
$12 = 100 'd' # "gdb_data_demo"第3个字符是'd'
# 5. 字符串格式打印str(默认展开字符串)
(gdb) print /s str
$13 = "gdb_data_demo"
2. 覆盖全局设置的选项(print [options] -- expr)
print支持临时覆盖set print的全局设置(如数组打印格式、地址显示),常用选项(🔶1-2521):
-address on/off:临时显示/隐藏地址;-array on/off:临时启用/禁用数组美化打印;-array-indexes on/off:临时显示/隐藏数组索引;-characters N:临时限制字符串打印长度。
实操:选项的使用
# 1. 打印数组str时,临时显示索引(覆盖全局关闭的情况)
(gdb) print -array-indexes on -- str
$14 = "gdb_data_demo"
{0x67 'g', 0x64 'd', 0x62 'b', 0x5f '_', 0x64 'd', 0x61 'a', 0x74 't', 0x61 'a', 0x5f '_', 0x64 'd', 0x65 'e', 0x6d 'm', 0x6f 'o', 0x0 '\000'}
# 2. 打印长字符串时,临时限制只打印5个字符
(gdb) print -characters 5 -- str
$15 = "gdb_d"
三、第三步:核心重难点2——内存底层查看(examine命令)
examine(缩写x) 是第十章的底层数据查看命令,直接读取内存地址的数据,不依赖变量名,适用于“无变量名的内存区域”(如堆内存、栈帧、硬件寄存器),是定位内存 corruption(如缓冲区溢出)的核心工具(🔶1-2518、🔶1-2520)。
3.1 x命令的语法与核心要素
语法:x[/f][n][u] addr,其中:
/f:输出格式(同print的格式符,如x十六进制、s字符串);n:打印的“单元数”(默认1);u:单元大小(b字节、h半字(2字节)、w字(4字节)、g双字(8字节),默认w);addr:内存地址(可是变量名、指针、直接地址)。
文档依据(🔶1-2520):x命令的核心是“按单元大小和格式,从指定地址开始读取n个单元的内存”,单元大小决定每次读取的字节数,格式决定显示方式。
3.2 实操:x命令的典型场景
场景1:查看变量的内存布局(以num=1024为例)
# 1. 查看num的地址(&num)
(gdb) print &num
$16 = (int *) 0x7fffffffde74
# 2. 以4字节(int大小)、十六进制格式,查看1个单元(num本身)
(gdb) x/wx &num
0x7fffffffde74: 0x00000400 # 1024的十六进制(小端存储,低字节在前)
# 3. 以字节为单位,查看num的4个字节(int占4字节)
(gdb) x/4bx &num
0x7fffffffde74: 0x00 0x04 0x00 0x00 # 小端存储:00 04 00 00 → 0x00000400=1024
场景2:查看数组的内存(以str为例)
# 1. 以字符串格式查看str(同print /s,但直接操作内存)
(gdb) x/s str
0x7fffffffde78: "gdb_data_demo"
# 2. 以字符(1字节)为单位,查看前5个字符
(gdb) x/5bc str
0x7fffffffde78: 103 'g' 100 'd' 98 'b' 95 '_' 100 'd'
场景3:查看结构体的内存布局(以stu为例)
# 1. 查看stu的地址
(gdb) print &stu
$17 = (Student *) 0x7fffffffde50
# 2. 以4字节为单位,查看stu的前3个单元(id占4字节,name占20字节,score占4字节)
(gdb) x/3wx &stu
0x7fffffffde50: 0x00000065 # id=101(0x65)
0x7fffffffde54: 0x69636141 # name的前4字节:"Alic"(小端:41 6c 69 63 → 'A' 'l' 'i' 'c')
0x7fffffffde58: 0x00000065 # name的后续字节(部分)
# 3. 查看score的内存(stu.score地址=stu地址+24字节:4(id)+20(name)=24)
(gdb) x/fx &stu.score
0x7fffffffde68: 0x42733333 # 95.5的IEEE754浮点表示
场景4:查看指针指向的内存(以p_num为例)
# 1. 查看p_num本身的地址和值(p_num是指针,存储num的地址)
(gdb) x/aw &p_num
0x7fffffffde70: 0x7fffffffde74 <num> # p_num的地址是0x7fffffffde70,值是num的地址
# 2. 解引用p_num,查看指向的内存(num的值)
(gdb) x/wd p_num
0x7fffffffde74: 1024 # 同*p_num的十进制值
四、第四步:核心重难点3——类型信息解析(whatis/ptype)
调试中常需确认变量的类型(如结构体成员、指针指向类型),第十章提供whatis(简要类型)和ptype(详细类型)命令,解决“变量是什么类型”“结构体包含哪些成员”的问题(🔶1-2518、🔶1-2520)。
4.1 whatis:简要类型信息
作用:显示变量或类型的“简短类型名”,不展开复杂类型(如结构体、模板)的内部成员。
实操:
# 1. 查看基本类型变量num的类型
(gdb) whatis num
type = int
# 2. 查看指针p_num的类型
(gdb) whatis p_num
type = int *
# 3. 查看结构体变量stu的类型(仅显示类型名,不展开成员)
(gdb) whatis stu
type = Student
# 4. 查看类型Student本身的简要信息
(gdb) whatis Student
type = struct Student
4.2 ptype:详细类型信息
作用:显示变量或类型的“完整类型定义”,展开复杂类型的内部结构(如结构体成员、数组大小、模板参数),是解析类型的核心命令(🔶1-2520)。
实操:
# 1. 查看结构体类型Student的完整定义
(gdb) ptype Student
type = struct Student {
int id;
char name[20];
float score;
}
# 2. 查看数组str的详细类型(包含大小)
(gdb) ptype str
type = char [13] # "gdb_data_demo"+'\0'共13个字符
# 3. 查看指针p_num的指向类型
(gdb) ptype *p_num
type = int
# 4. 查看函数参数类型(假设有函数int add(int a, int b))
(gdb) ptype add
type = int (int, int) # 函数类型:参数int,int,返回int
4.3 关键区别:whatis vs ptype
| 命令 | 输出内容 | 适用场景 |
|---|---|---|
whatis | 简短类型名,不展开细节 | 快速确认变量的基本类型(如是否为指针) |
ptype | 完整类型定义,展开复杂结构 | 解析结构体、数组、函数的详细类型 |
五、第五步:核心重难点4——打印格式自定义(set print系列命令)
默认打印格式可能存在“信息冗余”(如结构体紧凑显示)或“信息不足”(如数组不显示索引),第十章的set print系列命令可自定义打印行为,优化数据查看效率(🔶1-2521至🔶1-2531)。
5.1 常用set print命令与作用
| 命令 | 作用 | 示例 |
|---|---|---|
set print address on/off | 显示/隐藏变量地址 | set print address on → 打印变量时带地址 |
set print pretty on/off | 美化打印结构体/类(换行、缩进) | set print pretty on → 结构体成员分行显示 |
set print array on/off | 美化打印数组(分行、索引) | set print array on → 数组元素分行带索引 |
set print array-indexes on/off | 显示/隐藏数组索引 | set print array-indexes on → 显示索引 |
set print elements N | 限制数组/字符串打印的元素数(N=unlimited无限制) | set print elements 5 → 数组只打印前5个元素 |
set print null-stop on/off | 打印字符串时遇到’\0’是否停止 | set print null-stop on → 不打印’\0’后的垃圾数据 |
set print demangle on/off | 解码C++名字修饰(如_Znwm→operator new) | set print demangle on → 显示可读的C++函数名 |
5.2 实操:自定义打印格式
场景1:美化打印结构体(默认紧凑→美化)
# 默认打印(紧凑,成员在一行)
(gdb) print stu
$18 = {id = 101, name = "Alice", score = 95.5}
# 启用美化打印
(gdb) set print pretty on
# 重新打印(分行、缩进)
(gdb) print stu
$19 = {
id = 101,
name = "Alice",
score = 95.5
}
场景2:限制数组打印长度(避免长数组刷屏)
# 假设有长数组int arr[100] = {1,2,...,100}
(gdb) set print elements 5 # 只打印前5个元素
(gdb) print arr
$20 = {1, 2, 3, 4, 5, ...} # 省略后续元素
场景3:显示数组索引(默认不显示→显示)
# 默认打印str(无索引)
(gdb) print str
$21 = "gdb_data_demo"
# 启用数组索引显示
(gdb) set print array-indexes on
# 重新打印(带索引)
(gdb) print str
$22 = "gdb_data_demo"
{0x67 'g', 0x64 'd', 0x62 'b', 0x5f '_', 0x64 'd', 0x61 'a', 0x74 't', 0x61 'a', 0x5f '_', 0x64 'd', 0x65 'e', 0x6d 'm', 0x6f 'o', 0x0 '\000'}
场景4:显示变量地址(默认不显示→显示)
# 默认打印num(无地址)
(gdb) print num
$23 = 1024
# 启用地址显示
(gdb) set print address on
# 重新打印(带地址)
(gdb) print num
$24 = 1024 at 0x7fffffffde74
六、第六步:核心重难点5——高级数据处理(数组、字符串、结构体、指针)
第十章对复杂数据类型的处理是重点,需结合上述命令,解决“长数组查看”“结构体嵌套”“指针指向验证”等场景。
6.1 数组处理:print+set print array+x
实操:查看长数组的部分元素
// 新增长数组(添加到data_demo.c的main中)
int long_arr[100];
for (int i = 0; i < 100; i++) {
long_arr[i] = i * 2; // 元素为0,2,4,...,198
}
# 1. 查看前10个元素(带索引)
(gdb) set print array-indexes on
(gdb) print long_arr[0..9] # 切片语法,查看索引0-9
$25 = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
# 2. 用x命令查看索引10-14的元素(以十进制、4字节为单位)
(gdb) x/5wd &long_arr[10]
0x7fffffffdd28: 20 22 24 26 28
6.2 结构体嵌套处理:ptype+print+set print pretty
实操:嵌套结构体的查看
// 新增嵌套结构体(添加到data_demo.c)
typedef struct {
int year;
int month;
int day;
} Date;
typedef struct {
Student stu; // 嵌套Student结构体
Date birth; // 嵌套Date结构体
} StudentInfo;
// main中初始化
StudentInfo info = {{102, "Bob", 88.0f}, {2000, 9, 10}};
# 1. 查看嵌套结构体的类型定义
(gdb) ptype StudentInfo
type = struct StudentInfo {
Student stu;
Date birth;
}
# 2. 美化打印嵌套结构体
(gdb) set print pretty on
(gdb) print info
$26 = {
stu = {
id = 102,
name = "Bob",
score = 88
},
birth = {
year = 2000,
month = 9,
day = 10
}
}
6.3 指针与动态内存处理:print+x+ptype
实操:查看堆内存中的指针
// main中动态分配内存
int *p_dyn = malloc(4 * sizeof(int)); // 分配4个int的堆内存
for (int i = 0; i < 4; i++) {
p_dyn[i] = i + 10; // 元素为10,11,12,13
}
# 1. 查看指针p_dyn的类型和地址
(gdb) whatis p_dyn
type = int *
(gdb) print /a p_dyn
$27 = 0x55555576a2a0 # 堆内存地址(非栈地址)
# 2. 打印指针指向的数组(4个元素)
(gdb) print p_dyn[0..3]
$28 = {10, 11, 12, 13}
# 3. 用x命令查看堆内存的原始数据
(gdb) x/4wd p_dyn
0x55555576a2a0: 10 11 12 13
七、第七步:其他重难点——便利变量与动态类型
7.1 便利变量(Convenience Variables)
gdb提供临时变量(以$开头),用于存储中间结果,避免重复输入表达式(🔶1-2518):
$:存储上一次print或x的结果;$N:存储第N次print的结果(如$1是第一次print的结果);- 自定义便利变量:
set $var = expr(如set $sum = num + 10)。
实操:
# 1. 自定义便利变量,存储stu.score的两倍
(gdb) set $double_score = stu.score * 2
(gdb) print $double_score
$29 = 191
# 2. 使用$引用上一次结果($29=191)
(gdb) print $ + 5
$30 = 196
# 3. 使用$N引用历史结果($28是p_dyn[0..3]的结果)
(gdb) print $28[2] # 打印p_dyn[2]=12
$31 = 12
7.2 动态类型(Dynamic Type)
对于C++多态对象(带虚函数),ptype默认显示静态类型,需用set print object on显示动态类型(🔶1-2521):
// C++多态示例(假设为data_demo.cpp)
class Base { virtual void f() {} };
class Derived : public Base {};
Base *obj = new Derived(); // 静态类型Base*,动态类型Derived*
# 默认显示静态类型
(gdb) ptype obj
type = Base *
# 启用动态类型显示
(gdb) set print object on
# 显示动态类型
(gdb) ptype obj
type = Derived * # 正确显示动态类型
八、第十章核心调试流程总结(检查数据的完整步骤)
- 基础数据查看:用
print expr查看变量/表达式,whatis expr确认类型; - 格式调整:用
print /f expr临时改变格式,用set print自定义全局格式(如美化结构体、显示数组索引); - 底层内存验证:用
x[/fnu] addr查看内存原始数据,验证变量存储是否正确(如小端存储、浮点表示); - 复杂数据处理:
- 数组:
print arr[N..M]切片查看,set print elements限制长度; - 结构体:
ptype查看定义,set print pretty美化显示; - 指针:
print /a ptr查看指向,x验证指向内存;
- 数组:
- 临时结果存储:用便利变量(
set $var=expr)存储中间结果,提升效率。
5道中等难度实践题
题目1:print命令的多格式控制与表达式求值
场景描述
现有程序print_demo.c(含多类型变量,第十章🔶1-2518至🔶1-2520):
#include <stdio.h>
#include <string.h>
int main() {
int num = 255; // 整型(十进制255=十六进制0xff)
float pi = 3.1415926f; // 浮点型
char str[] = "gdb_print"; // 字符数组(字符串)
int *p_num = # // 指针
// 待调试的表达式
int expr_res = num * 2 + 10; // 预期255*2+10=520
printf("expr_res=%d\n", expr_res);
return 0;
}
要求:
- 用
gcc -g -O0 -o print_demo print_demo.c编译程序; - 启动GDB,在
printf前设断点(第12行),运行程序; - 用
print命令按以下5种格式打印num,验证输出正确性:- 十进制(默认)、十六进制(/x)、二进制(/t)、无符号十进制(/u)、地址格式(/a,打印&num);
- 用
print求值以下表达式,验证结果:pi * 2(浮点运算);strlen(str)(函数调用);*p_num + 50(指针解引用+算术运算);
- 说明每种格式的适用场景(参考🔶1-2520的格式符说明)。
核心考点
print命令的格式控制(🔶1-2520)、表达式求值(🔶1-2518)、多类型变量的打印适配
题目2:examine(x)命令的内存底层查看
场景描述
使用题目1的print_demo.c程序,聚焦变量的内存布局(第十章🔶1-2520)。要求:
- 启动GDB,运行程序至第12行断点;
- 用
x命令完成以下操作:- 以字节(b) 为单位,查看
num的4个字节(验证小端存储:255的十六进制0xff,小端应为ff 00 00 00); - 以字(w,4字节) 为单位,十六进制格式查看
p_num指向的内存(即num的值,应显示0x000000ff); - 以字符串(s) 格式查看
str的内存,验证字符串内容; - 以浮点(f) 格式查看
pi的内存,对比print pi的输出;
- 以字节(b) 为单位,查看
- 计算
num的内存地址与str的内存地址差,验证栈上变量的存储顺序(栈从高地址向低地址增长); - 说明
x命令与print命令的核心区别(参考🔶1-2518:x操作内存,print依赖变量名)。
核心考点
examine(x)命令的语法与应用(🔶1-2520)、小端存储验证、内存地址计算、x与print的差异
题目3:whatis/ptype的复杂类型解析
场景描述
现有含嵌套结构体与函数指针的程序type_demo.c(第十章🔶1-2520):
#include <stdio.h>
// 嵌套结构体:日期
typedef struct {
int year;
int month;
int day;
} Date;
// 外层结构体:用户信息
typedef struct {
char name[20];
int age;
Date birth; // 嵌套Date结构体
int (*calc)(int, int); // 函数指针(参数int,int,返回int)
} User;
// 函数:加法运算(供函数指针指向)
int add(int a, int b) {
return a + b;
}
int main() {
User user = {
"ZhangSan", 25,
{1999, 10, 15}, // birth
add // 函数指针指向add
};
return 0;
}
要求:
- 用
gcc -g -O0 -o type_demo type_demo.c编译程序; - 启动GDB,运行程序至main函数(第24行);
- 用
whatis查看以下元素的简要类型:user、user.birth、user.calc;
- 用
ptype查看以下元素的详细类型:User结构体(展开嵌套的Date成员);user.calc函数指针(显示参数与返回值类型);add函数(显示函数签名);
- 对比
whatis与ptype的输出差异,说明适用场景(参考🔶1-2520)。
核心考点
whatis的简要类型解析(🔶1-2520)、ptype的复杂类型展开(🔶1-2520)、嵌套结构体与函数指针的类型查看
题目4:set print系列命令的格式自定义
场景描述
使用题目3的type_demo.c程序,聚焦print格式自定义(第十章🔶1-2521至🔶1-2531)。要求:
- 启动GDB,运行程序至main函数;
- 按以下步骤自定义打印格式,对比输出差异:
- 步骤1:默认格式打印
user(观察紧凑显示的结构体); - 步骤2:用
set print pretty on启用结构体美化打印,重新打印user(观察分行缩进); - 步骤3:用
set print array-indexes on显示数组索引,打印user.name(观察字符数组的索引与值); - 步骤4:用
set print elements 5限制数组打印长度,打印一个长数组(新增int long_arr[10] = {0,1,2,3,4,5,6,7,8,9});
- 步骤1:默认格式打印
- 用
show print系列命令查看当前配置(如show print pretty、show print elements); - 说明
set print系列命令的核心价值(参考🔶1-2521:优化复杂数据的可读性)。
核心考点
set print pretty的结构体美化(🔶1-2521)、set print array-indexes的数组索引显示(🔶1-2521)、set print elements的数组长度限制(🔶1-2521)
题目5:便利变量与动态内存的数据查看
场景描述
现有含动态内存分配的程序conv_var_demo.c(第十章🔶1-2518:便利变量;🔶1-2520:动态内存查看):
#include <stdio.h>
#include <stdlib.h>
int main() {
// 动态分配10个int的堆内存
int *p_dyn = (int *)malloc(10 * sizeof(int));
if (p_dyn == NULL) return 1;
// 初始化堆内存:p_dyn[i] = i*3
for (int i = 0; i < 10; i++) {
p_dyn[i] = i * 3; // 预期值:0,3,6,...,27
}
// 待调试的临时计算
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += p_dyn[i]; // 预期sum=0+3+...+27=135
}
free(p_dyn);
return 0;
}
要求:
- 用
gcc -g -O0 -o conv_var_demo conv_var_demo.c编译程序; - 启动GDB,在
free(p_dyn)前设断点(第18行),运行程序; - 用便利变量完成以下操作:
- 定义便利变量
$mid_idx = 5,打印p_dyn[$mid_idx](预期15); - 定义便利变量
$total = sum * 2,打印$total(预期270); - 用
$引用上一次print的结果(如print $ + 5,预期275);
- 定义便利变量
- 用
x命令查看堆内存中p_dyn[3..7]的原始数据(以十进制、4字节为单位); - 说明便利变量的核心作用(参考🔶1-2518:存储临时结果,减少重复输入)。
核心考点
便利变量的定义与使用(🔶1-2518)、动态内存的x命令查看(🔶1-2520)、临时计算结果的存储
题目详解答案
题目1:print命令的多格式控制与表达式求值
详细步骤
-
编译与启动GDB:
gcc -g -O0 -o print_demo print_demo.c gdb ./print_demo (gdb) break print_demo.c:12 # 在printf前设断点 Breakpoint 1 at 0x4005c7: file print_demo.c, line 12. (gdb) run Starting program: ./print_demo Breakpoint 1, main () at print_demo.c:12 12 printf("expr_res=%d\n", expr_res); -
多格式打印num(🔶1-2520):
# 1. 十进制(默认):255 (gdb) print num $1 = 255 # 2. 十六进制(/x):0xff (gdb) print /x num $2 = 0xff # 3. 二进制(/t):11111111 (gdb) print /t num $3 = 11111111 # 4. 无符号十进制(/u):255(与十进制一致,因num为正数) (gdb) print /u num $4 = 255 # 5. 地址格式(/a):打印&num的地址(带符号关联) (gdb) print /a &num $5 = 0x7fffffffde74 <num> -
表达式求值(🔶1-2518):
# 1. 浮点运算:pi*2 ≈6.2831852 (gdb) print pi * 2 $6 = 6.283185207366257 # 2. 函数调用:strlen(str) = 9("gdb_print"长度) (gdb) print strlen(str) $7 = 9 # 3. 指针解引用+算术运算:*p_num+50=255+50=305 (gdb) print *p_num + 50 $8 = 305 -
格式适用场景(🔶1-2520):
- 十进制(默认):日常整型变量查看;
- 十六进制(/x):内存地址、二进制数据(如寄存器值);
- 二进制(/t):位运算结果、标志位(如权限位);
- 无符号十进制(/u):无符号整型(如unsigned int);
- 地址格式(/a):指针变量,明确指向的符号(如变量/函数)。
题目2:examine(x)命令的内存底层查看
详细步骤
-
启动GDB并运行至断点(同题目1步骤1)。
-
用x命令查看内存(🔶1-2520):
# 1. 字节单位查看num(验证小端:0xff存储为ff 00 00 00) (gdb) x/4bx &num # 4个字节(b),十六进制(x) 0x7fffffffde74: 0xff 0x00 0x00 0x00 # 小端存储:低字节在前 # 2. 字单位查看p_num指向的内存(num的值) (gdb) x/wx p_num # 1个字(w=4字节),十六进制(x) 0x7fffffffde74: 0x000000ff # 与num的十六进制一致 # 3. 字符串格式查看str (gdb) x/s str 0x7fffffffde78: "gdb_print" # 与print /s str结果一致 # 4. 浮点格式查看pi的内存 (gdb) x/fx &pi # 浮点格式(f),十六进制(x) 0x7fffffffde7c: 0x40490fdb # pi的IEEE754十六进制表示 (gdb) print /x pi # 对比print的浮点十六进制输出 $9 = 0x40490fdb # 与x命令结果一致 -
内存地址差计算(验证栈增长方向):
# 查看num和str的地址 (gdb) print &num $10 = (int *) 0x7fffffffde74 (gdb) print &str $11 = (char (*)[9]) 0x7fffffffde78 # 地址差:str地址(0x7fffffffde78) - num地址(0x7fffffffde74)=4字节 # 结论:栈从高地址向低地址增长,num(先定义)在高地址,str(后定义)在低地址 -
x与print的核心区别(🔶1-2518):
x:直接操作内存地址,不依赖变量名(适用于无变量名的内存区域,如堆、栈帧);print:依赖变量名与调试信息,按类型解析数据(适用于已知变量的查看);
例:可通过x查看0x7fffffffde74的内存,但需print num才能关联该地址为num变量。
题目3:whatis/ptype的复杂类型解析
详细步骤
-
编译与启动GDB:
gcc -g -O0 -o type_demo type_demo.c gdb ./type_demo (gdb) break type_demo.c:24 # 在main函数初始化后设断点 Breakpoint 1 at 0x40058d: file type_demo.c, line 24. (gdb) run Starting program: ./type_demo Breakpoint 1, main () at type_demo.c:24 24 return 0; -
用whatis查看简要类型(🔶1-2520):
# 1. user的简要类型:User结构体 (gdb) whatis user type = User # 2. user.birth的简要类型:Date结构体 (gdb) whatis user.birth type = Date # 3. user.calc的简要类型:函数指针 (gdb) whatis user.calc type = int (*)(int, int) -
用ptype查看详细类型(🔶1-2520):
# 1. User结构体(展开嵌套的Date) (gdb) ptype User type = struct User { char name[20]; int age; Date birth; // 嵌套结构体 int (*calc)(int, int); // 函数指针 } # 2. user.calc函数指针(详细参数与返回值) (gdb) ptype user.calc type = int (*)(int, int) # 参数int,int,返回int # 3. add函数的签名 (gdb) ptype add type = int (int, int) # 函数类型:参数int,int,返回int -
whatis与ptype的差异(🔶1-2520):
命令 输出内容 适用场景 whatis简短类型名,不展开细节 快速确认变量的基础类型(如是否为结构体/指针) ptype完整类型定义,展开嵌套结构 解析复杂类型(如嵌套结构体、函数指针)的内部细节
题目4:set print系列命令的格式自定义
详细步骤
-
编译与启动GDB(同题目3步骤1),新增长数组初始化(在main函数内添加):
int long_arr[10] = {0,1,2,3,4,5,6,7,8,9}; -
自定义打印格式并对比(🔶1-2521):
# 步骤1:默认格式打印user(紧凑显示) (gdb) print user $12 = {name = "ZhangSan", age = 25, birth = {year = 1999, month = 10, day = 15}, calc = 0x40052d <add>} # 步骤2:启用结构体美化打印(分行缩进) (gdb) set print pretty on (gdb) print user $13 = { name = "ZhangSan", age = 25, birth = { year = 1999, month = 10, day = 15 }, calc = 0x40052d <add> } # 步骤3:显示数组索引,打印user.name (gdb) set print array-indexes on (gdb) print user.name $14 = "ZhangSan" {0x5a 'Z', 0x68 'h', 0x61 'a', 0x6e 'n', 0x67 'g', 0x53 'S', 0x61 'a', 0x6e 'n', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000', 0x0 '\000'} # 步骤4:限制数组打印长度为5,打印long_arr (gdb) set print elements 5 (gdb) print long_arr $15 = {0, 1, 2, 3, 4, ...} # 仅显示前5个元素,省略后续 -
查看当前配置(🔶1-2521):
(gdb) show print pretty Pretty-printing of structures is on. (gdb) show print array-indexes Array indexes printing is on. (gdb) show print elements Limit on array elements per print is 5. -
set print的核心价值(🔶1-2521):
通过自定义格式(美化、索引、长度限制),将复杂数据(如嵌套结构体、长数组)的打印结果更易读,避免信息冗余或缺失,提升调试效率。
题目5:便利变量与动态内存的数据查看
详细步骤
-
编译与启动GDB:
gcc -g -O0 -o conv_var_demo conv_var_demo.c gdb ./conv_var_demo (gdb) break conv_var_demo.c:18 # 在free前设断点 Breakpoint 1 at 0x400627: file conv_var_demo.c, line 18. (gdb) run Starting program: ./conv_var_demo Breakpoint 1, main () at conv_var_demo.c:18 18 free(p_dyn); -
定义与使用便利变量(🔶1-2518):
# 1. 定义$mid_idx=5,打印p_dyn[5](5*3=15) (gdb) set $mid_idx = 5 (gdb) print p_dyn[$mid_idx] $1 = 15 # 2. 定义$total=sum*2,打印$total(sum=135→270) (gdb) set $total = sum * 2 (gdb) print $total $2 = 270 # 3. 用$引用上一次结果($2=270→270+5=275) (gdb) print $ + 5 $3 = 275 -
用x命令查看堆内存(🔶1-2520):
# 查看p_dyn[3..7](索引3-7,共5个元素:9,12,15,18,21) (gdb) x/5wd &p_dyn[3] # 5个单元,字(w),十进制(d) 0x55555576a2a0: 9 12 15 18 21 # 与预期一致 -
便利变量的核心作用(🔶1-2518):
存储调试过程中的临时结果(如索引、计算值),避免重复输入复杂表达式(如p_dyn[5]可简化为p_dyn[$mid_idx]),尤其适用于多次复用同一中间值的场景。
5道Hard难度实践题
题目1:C++嵌套模板结构体的类型解析与函数指针验证
场景描述
现有C++嵌套模板程序template_nested.cpp(第十章🔶1-2520:复杂类型解析;🔶1-2518:函数指针打印):
#include <iostream>
// 内层模板:存储数据与运算函数指针
template <typename T>
struct Data {
T value;
T (*op)(T, T); // 函数指针:参数T,T,返回T
};
// 外层模板:嵌套Data,支持数据组合
template <typename T1, typename T2>
struct Pair {
Data<T1> first; // 嵌套Data<T1>
Data<T2> second; // 嵌套Data<T2>
void print() { // 成员函数
std::cout << "First: " << first.value << ", Second: " << second.value << std::endl;
}
};
// 辅助函数:加法(供int类型函数指针指向)
int add_int(int a, int b) { return a + b; }
// 辅助函数:乘法(供double类型函数指针指向)
double mul_double(double a, double b) { return a * b; }
int main() {
// 初始化嵌套模板对象:Pair<int, double>
Pair<int, double> p = {
{10, add_int}, // first: Data<int>,value=10,op=add_int
{3.14, mul_double} // second: Data<double>,value=3.14,op=mul_double
};
p.print();
return 0;
}
要求:
- 用
g++ -g -O0 -o template_nested template_nested.cpp编译程序; - 启动GDB,在
p.print()处设断点(第28行),运行程序; - 用ptype展开
Pair<int, double>的完整类型,验证:- 嵌套
Data<int>和Data<double>的成员; - 函数指针
op的参数与返回值类型;
- 嵌套
- 用print按以下格式验证函数指针指向与调用结果:
- 以地址格式(/a)打印
p.first.op和p.second.op,确认指向add_int和mul_double; - 调用函数指针(
p.first.op(5, 3)),验证返回值(8);
- 以地址格式(/a)打印
- 用set print pretty on美化打印
p,观察嵌套模板的结构体布局; - 说明嵌套模板类型解析的核心难点(参考🔶1-2520:复杂类型需明确模板参数与成员关联)。
核心考点
嵌套模板的ptype类型展开(🔶1-2520)、函数指针的print格式与调用(🔶1-2518)、结构体美化打印(🔶1-2521)
题目2:结构体内存对齐的底层查看与x命令深度分析
场景描述
现有含内存对齐的C程序mem_align.c(第十章🔶1-2520:x命令内存查看;🔶1-2518:结构体类型解析):
#include <stdio.h>
// 结构体1:默认对齐(gcc默认4字节对齐)
struct AlignDefault {
char c; // 1字节
int i; // 4字节(因对齐,与c间有3字节间隙)
short s; // 2字节(与i无间隙,整体对齐到4字节,总大小12字节)
};
// 结构体2:强制1字节对齐(gcc __attribute__((packed)))
struct AlignPacked __attribute__((packed)) {
char c; // 1字节
int i; // 4字节(无间隙,与c紧连)
short s; // 2字节(无间隙,总大小7字节)
};
int main() {
struct AlignDefault d = {'A', 1024, 256};
struct AlignPacked p = {'B', 2048, 512};
return 0;
}
要求:
- 用
gcc -g -O0 -o mem_align mem_align.c编译程序; - 启动GDB,在
return 0处设断点(第20行),运行程序; - 用ptype查看两个结构体的类型,确认成员定义;
- 用x命令按字节(b)查看两个结构体的内存布局,验证:
AlignDefault的间隙(c与i间3字节,值为0x00);AlignPacked无间隙(c与i紧连);
- 计算两个结构体的实际内存大小(通过x命令的地址差),对比
sizeof的输出; - 用print按十六进制格式打印结构体成员,关联x命令的内存数据;
- 说明内存对齐对x命令查看的影响(参考🔶1-2520:需按对齐规则解析内存单元)。
核心考点
结构体内存对齐的x命令验证(🔶1-2520)、sizeof与实际内存大小对比、x命令的字节级查看(🔶1-2520)
题目3:C++多态的动态类型解析与虚函数表查看
场景描述
现有C++多态程序polymorphism.cpp(第十章🔶1-2521:set print object;🔶1-2520:ptype动态类型):
#include <iostream>
// 基类(带虚函数)
class Base {
public:
virtual void show() { std::cout << "Base::show()" << std::endl; }
virtual ~Base() {}
int base_val = 100;
};
// 派生类(重写虚函数)
class Derived : public Base {
public:
void show() override { std::cout << "Derived::show()" << std::endl; }
double derived_val = 3.14;
};
int main() {
Base* ptr = new Derived(); // 基类指针指向派生类(动态类型Derived*)
ptr->show(); // 多态调用:Derived::show()
delete ptr;
return 0;
}
要求:
- 用
g++ -g -O0 -o polymorphism polymorphism.cpp编译程序; - 启动GDB,在
ptr->show()处设断点(第19行),运行程序; - 未启用
set print object时,用ptype和print查看ptr的类型,观察静态类型(Base*); - 启用
set print object on,重新用ptype和print查看ptr,验证动态类型(Derived*); - 用x命令查看
ptr指向的虚函数表(vtable):- 虚表地址存储在对象首地址,查看前8字节(64位)获取虚表指针;
- 查看虚表中的第一个函数地址(Derived::show());
- 打印
ptr的成员(base_val和derived_val),验证动态类型的成员访问; - 说明
set print object的核心作用(参考🔶1-2521:显示多态对象的动态类型)。
核心考点
多态动态类型解析(🔶1-2521)、虚函数表的x命令查看(🔶1-2520)、set print object的作用(🔶1-2521)
题目4:超大结构体数组的print优化与切片查看
场景描述
现有超大结构体数组程序large_array.cpp(第十章🔶1-2521:set print elements/array-indexes;🔶1-2518:便利变量):
#include <stdio.h>
// 结构体:包含多类型成员
struct SensorData {
int id; // 传感器ID
float temp; // 温度
double pressure; // 压力
char status; // 状态('O'=正常,'E'=异常)
};
// 超大数组:1000个元素(模拟工业传感器数据)
SensorData data[1000];
int main() {
// 初始化:id=i,temp=20+i*0.1,pressure=1013+i*0.5,status='O'
for (int i = 0; i < 1000; i++) {
data[i].id = i;
data[i].temp = 20.0f + i * 0.1f;
data[i].pressure = 1013.0 + i * 0.5;
data[i].status = 'O';
}
// 故意设置异常数据:id=500的status='E'
data[500].status = 'E';
return 0;
}
要求:
- 用
g++ -g -O0 -o large_array large_array.cpp编译程序; - 启动GDB,在
return 0处设断点(第22行),运行程序; - 未优化时直接
print data,观察输出刷屏问题; - 用set print系列命令优化显示:
set print elements 10:限制数组打印长度为10;set print array-indexes on:显示数组索引;set print pretty on:美化结构体成员;- 重新
print data,验证优化效果;
- 用便利变量切片查看异常数据(id=500):
- 定义
$start = 498,$end = 502; - 打印
data[$start..$end],定位status='E’的元素;
- 定义
- 用x命令查看id=500的
status内存(1字节),验证值为’E’(ASCII 69); - 说明
set print优化对超大数组的核心价值(参考🔶1-2521:避免信息冗余,聚焦关键数据)。
核心考点
超大数组的print优化(🔶1-2521)、便利变量的切片应用(🔶1-2518)、x命令的单个成员查看(🔶1-2520)
题目5:动态二维数组的便利变量计算与内存验证
场景描述
现有动态二维数组程序dyn_2d_array.c(第十章🔶1-2518:便利变量;🔶1-2520:x命令动态内存查看):
#include <stdio.h>
#include <stdlib.h>
int main() {
// 动态二维数组:3行4列(行指针数组+每行数据)
int rows = 3, cols = 4;
int** arr = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
// 初始化:arr[i][j] = i*cols + j
for (int j = 0; j < cols; j++) {
arr[i][j] = i * cols + j; // 预期值:0-11
}
}
// 待调试:验证第2行第3列(索引1,2)的值(1*4+2=6)
return 0;
}
要求:
- 用
gcc -g -O0 -o dyn_2d_array dyn_2d_array.c编译程序; - 启动GDB,在
return 0处设断点(第17行),运行程序; - 用便利变量计算动态二维数组的元素地址:
- 定义
$row_idx = 1(第2行),$col_idx = 2(第3列); - 计算元素地址:
$elem_addr = arr[$row_idx] + $col_idx; - 打印
$elem_addr的地址格式(/a),验证指向的内存;
- 定义
- 用print打印
arr[$row_idx][$col_idx],验证值为6; - 用x命令查看第2行的所有元素(4个int,十进制):
- 地址为
arr[$row_idx],查看4个单元(/4wd);
- 地址为
- 计算并验证动态内存的总大小(rowscolssizeof(int) + rowssizeof(int)),用x命令查看行指针数组的内存;
- 说明便利变量在动态数组中的核心作用(参考🔶1-2518:简化复杂地址计算,避免重复输入)。
核心考点
动态二维数组的地址计算(🔶1-2518)、x命令的行级内存查看(🔶1-2520)、便利变量的表达式简化(🔶1-2518)
题目详解答案
题目1:C++嵌套模板结构体的类型解析与函数指针验证
详细步骤
-
编译与启动GDB:
g++ -g -O0 -o template_nested template_nested.cpp gdb ./template_nested (gdb) break template_nested.cpp:28 # 在p.print()处设断点 Breakpoint 1 at 0x4007a6: file template_nested.cpp, line 28. (gdb) run Starting program: ./template_nested Breakpoint 1, main () at template_nested.cpp:28 28 p.print(); -
ptype展开嵌套模板类型(🔶1-2520):
# 展开Pair<int, double>的完整类型 (gdb) ptype Pair<int, double> type = struct Pair<int, double> { struct Data<int> first; // 嵌套Data<int> struct Data<double> second; // 嵌套Data<double> void print(); // 成员函数 } # 展开Data<int>(验证函数指针op的类型) (gdb) ptype Data<int> type = struct Data<int> { int value; int (*op)(int, int); // 函数指针:int(int,int) } # 展开Data<double> (gdb) ptype Data<double> type = struct Data<double> { double value; double (*op)(double, double); // 函数指针:double(double,double) } -
print验证函数指针指向与调用(🔶1-2518):
# 1. 地址格式打印函数指针,确认指向add_int和mul_double (gdb) print /a p.first.op $1 = 0x4006e6 <add_int(int, int)> # 指向add_int (gdb) print /a p.second.op $2 = 0x4006f0 <mul_double(double, double)> # 指向mul_double # 2. 调用函数指针,验证返回值(add_int(5,3)=8) (gdb) print p.first.op(5, 3) $3 = 8 # 调用mul_double(2.0, 3.0)=6.0 (gdb) print p.second.op(2.0, 3.0) $4 = 6.0 -
美化打印嵌套结构体(🔶1-2521):
(gdb) set print pretty on (gdb) print p $5 = { first = { value = 10, op = 0x4006e6 <add_int(int, int)> }, second = { value = 3.1400000000000001, op = 0x4006f0 <mul_double(double, double)> } } -
嵌套模板解析难点(🔶1-2520):
需明确模板参数类型(如int/double)与成员关联(函数指针的参数/返回值需与模板类型匹配),ptype需逐层展开才能确认嵌套结构,print需结合地址格式验证函数指针指向的正确性,避免因模板实例化歧义导致的类型误判。
题目2:结构体内存对齐的底层查看与x命令深度分析
详细步骤
-
编译与启动GDB:
gcc -g -O0 -o mem_align mem_align.c gdb ./mem_align (gdb) break mem_align.c:20 # 在return 0处设断点 Breakpoint 1 at 0x40057d: file mem_align.c, line 20. (gdb) run Starting program: ./mem_align Breakpoint 1, main () at mem_align.c:20 20 return 0; -
ptype确认结构体定义(🔶1-2520):
(gdb) ptype struct AlignDefault type = struct AlignDefault { char c; int i; short s; } (gdb) ptype struct AlignPacked type = struct AlignPacked { char c; int i; short s; } __attribute__((packed)) # 强制1字节对齐 -
x命令查看内存布局(🔶1-2520):
# 1. AlignDefault:默认4字节对齐(总大小12字节) (gdb) print &d $1 = (struct AlignDefault *) 0x7fffffffde60 (gdb) x/12bx &d # 12字节,十六进制 0x7fffffffde60: 0x41 0x00 0x00 0x00 # c='A'(0x41) + 3字节间隙 0x7fffffffde64: 0x00 0x04 0x00 0x00 # i=1024(0x400) 0x7fffffffde68: 0x00 0x01 0x00 0x00 # s=256(0x100) + 2字节间隙(对齐到4字节) # 2. AlignPacked:1字节对齐(总大小7字节) (gdb) print &p $2 = (struct AlignPacked *) 0x7fffffffde59 (gdb) x/7bx &p # 7字节,十六进制 0x7fffffffde59: 0x42 # c='B'(0x42) 0x7fffffffde5a: 0x00 0x08 0x00 0x00 # i=2048(0x800)(无间隙) 0x7fffffffde5e: 0x00 0x02 # s=512(0x200)(无间隙) -
大小计算与sizeof对比(🔶1-2518):
# AlignDefault:地址差=0x7fffffffde60+12 - 0x7fffffffde60=12字节 (gdb) print sizeof(d) $3 = 12 # AlignPacked:地址差=0x7fffffffde59+7 - 0x7fffffffde59=7字节 (gdb) print sizeof(p) $4 = 7 -
内存对齐对x命令的影响(🔶1-2520):
默认对齐会产生内存间隙(如AlignDefault的3字节间隙),x命令需按“结构体总大小”查看才能完整覆盖所有成员;强制对齐(packed)无间隙,内存紧凑存储,x命令可按“成员大小之和”查看。调试时需先明确对齐规则,避免因间隙导致的内存数据误读。
题目3:C++多态的动态类型解析与虚函数表查看
详细步骤
-
编译与启动GDB:
g++ -g -O0 -o polymorphism polymorphism.cpp gdb ./polymorphism (gdb) break polymorphism.cpp:19 # 在ptr->show()处设断点 Breakpoint 1 at 0x400816: file polymorphism.cpp, line 19. (gdb) run Starting program: ./polymorphism Breakpoint 1, main () at polymorphism.cpp:19 19 ptr->show(); -
未启用set print object:静态类型(🔶1-2521):
# ptype显示静态类型Base* (gdb) ptype ptr type = Base * # print仅显示Base成员(base_val),隐藏Derived成员(derived_val) (gdb) print *ptr $1 = { _vptr.Base = 0x4009d0 <vtable for Derived+16>, base_val = 100 } -
启用set print object:动态类型(🔶1-2521):
(gdb) set print object on # ptype显示动态类型Derived* (gdb) ptype ptr type = Derived * # print显示Derived所有成员(base_val+derived_val) (gdb) print *ptr $2 = (Derived) { <Base> = { _vptr.Base = 0x4009d0 <vtable for Derived+16>, base_val = 100 }, members of Derived: derived_val = 3.1400000000000001 } -
x命令查看虚函数表(🔶1-2520):
# 1. 查看对象首地址(存储虚表指针) (gdb) print /a ptr $3 = 0x55555576a2c0 (gdb) x/aw ptr # 查看虚表指针(8字节,64位) 0x55555576a2c0: 0x4009d0 <vtable for Derived+16> # 2. 查看虚表中的第一个函数地址(Derived::show()) (gdb) x/aw 0x4009d0 0x4009d0 <vtable for Derived+16>: 0x4007d6 <Derived::show()> -
set print object的核心作用(🔶1-2521):
多态对象的基类指针默认显示静态类型(Base*),set print object on强制GDB通过虚函数表识别动态类型(Derived*),从而完整显示派生类的成员与虚函数,避免因静态类型导致的调试信息缺失。
题目4:超大结构体数组的print优化与切片查看
详细步骤
-
编译与启动GDB:
g++ -g -O0 -o large_array large_array.cpp gdb ./large_array (gdb) break large_array.cpp:22 # 在return 0处设断点 Breakpoint 1 at 0x4006a7: file large_array.cpp, line 22. (gdb) run Starting program: ./large_array Breakpoint 1, main () at large_array.cpp:22 22 return 0; -
未优化的print问题(🔶1-2521):
(gdb) print data # 直接打印1000元素,输出刷屏(省略中间内容) $1 = {{id = 0, temp = 20, pressure = 1013, status = 79 'O'}, {id = 1, temp = 20.100000381469727, pressure = 1013.5, status = 79 'O'}, ...} -
set print优化显示(🔶1-2521):
# 1. 限制数组长度+显示索引+美化结构体 (gdb) set print elements 10 (gdb) set print array-indexes on (gdb) set print pretty on # 2. 重新打印,仅显示前10个元素,带索引和美化 (gdb) print data $2 = { [0] = { id = 0, temp = 20, pressure = 1013, status = 79 'O' }, [1] = { id = 1, temp = 20.100000381469727, pressure = 1013.5, status = 79 'O' }, ..., # 省略中间元素,共显示10个 [9] = { id = 9, temp = 20.899999618530273, pressure = 1017.5, status = 79 'O' }, ... # 提示省略剩余990个元素 } -
便利变量切片查看异常数据(🔶1-2518):
# 定义便利变量,切片查看id=498-502 (gdb) set $start = 498 (gdb) set $end = 502 (gdb) print data[$start..$end] $3 = { [498] = {id = 498, temp = 69.80000305175781, pressure = 1252, status = 79 'O'}, [499] = {id = 499, temp = 69.9000015258789, pressure = 1252.5, status = 79 'O'}, [500] = {id = 500, temp = 70, pressure = 1253, status = 69 'E'}, # 异常数据 [501] = {id = 501, temp = 70.10000610351562, pressure = 1253.5, status = 79 'O'}, [502] = {id = 502, temp = 70.19999694824219, pressure = 1254, status = 79 'O'} } -
x命令验证异常成员(🔶1-2520):
# 查看data[500].status的内存(1字节,ASCII 'E'=69) (gdb) print /a &data[500].status $4 = 0x55555576c073 (gdb) x/bx 0x55555576c073 0x55555576c073: 0x45 # 0x45=69='E' -
print优化的核心价值(🔶1-2521):
对超大数组,set print elements避免输出刷屏,array-indexes便于定位元素,pretty提升结构体可读性,结合便利变量切片可快速聚焦关键数据(如异常元素),大幅提升调试效率。
题目5:动态二维数组的便利变量计算与内存验证
详细步骤
-
编译与启动GDB:
gcc -g -O0 -o dyn_2d_array dyn_2d_array.c gdb ./dyn_2d_array (gdb) break dyn_2d_array.c:17 # 在return 0处设断点 Breakpoint 1 at 0x400637: file dyn_2d_array.c, line 17. (gdb) run Starting program: ./dyn_2d_array Breakpoint 1, main () at dyn_2d_array.c:17 17 return 0; -
便利变量计算元素地址(🔶1-2518):
# 定义行/列索引,计算元素地址 (gdb) set $row_idx = 1 (gdb) set $col_idx = 2 (gdb) set $elem_addr = arr[$row_idx] + $col_idx # 地址格式打印,验证指向的内存 (gdb) print /a $elem_addr $1 = 0x55555576a2d8 # 元素arr[1][2]的地址 -
print验证元素值(🔶1-2518):
(gdb) print arr[$row_idx][$col_idx] $2 = 6 # 符合预期:1*4+2=6 -
x命令查看第2行所有元素(🔶1-2520):
# 第2行的首地址=arr[$row_idx] (gdb) print /a arr[$row_idx] $3 = 0x55555576a2d0 # 查看4个int(cols=4),十进制 (gdb) x/4wd 0x55555576a2d0 0x55555576a2d0: 4 5 6 7 # 第2行元素:4(1*4+0)、5、6、7 -
动态内存总大小计算(🔶1-2518):
# 总大小=行指针数组(3*8=24字节) + 数据(3*4*4=48字节)=72字节 (gdb) set $ptr_size = rows * sizeof(int*) (gdb) set $data_size = rows * cols * sizeof(int) (gdb) print $ptr_size + $data_size $4 = 72 # x命令查看行指针数组(3个指针,每个8字节) (gdb) x/3aw arr 0x55555576a2a0: 0x55555576a2b0 0x55555576a2d0 0x55555576a2f0 # 3行的首地址 -
便利变量的核心作用(🔶1-2518):
动态二维数组的地址计算需“行指针+列偏移”,便利变量(如$row_idx/$col_idx)可简化重复计算,避免手动输入arr[1][2]或arr[1]+2,尤其适用于多次复用同一行/列索引的场景,提升调试效率。
977

被折叠的 条评论
为什么被折叠?



