变量检查与调用栈 —— bug 的现场取证
变量检查
程序停在断点时,所有当前作用域内的变量都可以查看。
基本类型
int count = 42; ← 显示 count = 42
double ratio = 3.14159; ← 显示 ratio = 3.14159
bool active = true; ← 显示 active = true
char *msg = "hello"; ← 显示 msg = "hello"
指针
Node *node = getHead(); ← 显示 node = 0x7abc1234
如果 node 不是 null,点击展开可以看 node->data 等成员
STL 容器
部分 Cxxdroid 版本可能对 STL 容器的显示不太友好——gdb 需要 Python pretty printer 支持才能把 std::vector<int>{1, 2, 3} 正确展开显示。如果显示成一堆内部迭代器或分配器对象,属正常现象。
自定义结构体 / 类
struct User {
int id;
std::string name;
float score;
};
User u = {1, "Alice", 95.5};
点击 u 可以展开看到三个成员各自的值。
调用栈(Call Stack)
调用栈记录了从程序入口到当前断点的函数调用链:
[0] getUserProfile(userId=42) ← 当前停在这里
[1] loadUserData(userId=42) ← getUserProfile 的调用者
[2] handleHttpRequest(request=...) ← loadUserData 的调用者
[3] main() ← 程序入口
从下往上看:main 调了 handleHttpRequest,后者调了 loadUserData,后者又调了 getUserProfile——你就在 getUserProfile 里。
调用栈的两个用途
回溯调用者:你当前在某个深嵌套的函数里,想看看到底是谁调进来的。点击调用栈里的某一帧(比如 [1] loadUserData),调试器会跳回到那一行——你就能看到 loadUserData 当时的局部变量和参数值。
定位空指针来源:程序在 deref(ptr) 崩了,调用栈显示是从 processOrder → loadUser → deref 过来的。到 loadUser 那一帧看,ptr 在传给 deref 之前就是 null 了——说明问题在 loadUser 里,不是 deref 本身。
查看数组 / 内存
gdb 可以直接查看一段内存区域的值:
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
调试面板里点击 arr,应该能看到 10 个元素的值。如果看到的只是数组首地址,可以尝试手动添加 Watch 表达式:arr[0]@10(gdb 特定语法)。
Watch 窗口
Watch 窗口允许你手动添加任意表达式来持续观察:
- 某个还没声明的变量(还没运行到那一行就会报 undefined)
- 复杂表达式(
user.score * 2 + bonus) - 地址解引用(
*ptr查看指针指向的值)
表达式的值会随着单步执行实时更新——每 Step 一次就刷新一次。
如果 Cxxdroid 版本没有独立的 Watch 面板,试试在变量查看区域找「添加表达式」按钮。
常见坑
编译器优化导致变量被优化掉
如果 Debug 模式没正确关闭优化(-O2),编译器可能把某些变量直接优化进寄存器、甚至直接消除。此时调试面板里会显示 <optimized out>,意思是「这个变量在编译后已经不存在了」。
解决方法:在 Debug 构建配置里确认:
- 开启
-g(生成调试符号) - 关闭
-O2或设为-O0(不优化)
STL 容器显示乱码
如前所述,gdb 需要 pretty printer 才能正确显示 STL 容器。如果显示成内部实现细节,可以:
- 手动查看容器的
size()和operator[]方法返回值 - 或者直接在 Watch 窗口输入表达式
vec.size()和vec[0]
指针显示地址但不显示值
ptr = 0x12345678 能看到地址,但展开后成员显示 <memory unavailable>——要么地址无效(野指针)、要么指针类型不对。