空指针解引用崩溃时bt显示的是sigsegv触发点而非根因,需用bt full结合f 1/f 2上溯调用链、info args和info registers分析上游参数与寄存器值,定位未判空、误释放或错误传参环节。

崩溃时 bt 显示的是空指针触发点,不是真正出问题的地方
空指针解引用(如 *NULL 或 obj->method() 中 obj 为 nullptr)本身不会“主动”崩溃,而是在 CPU 尝试读写非法地址时由内核抛出 SIGSEGV。GDB 的 bt 默认显示的是信号发生时的栈帧——也就是那个 *null_ptr 所在行,但它往往只是“最后一击”,真正该追查的是谁把 nullptr 传进来、谁没做判空、谁提前 delete 了但没置空。
用 bt full + f N 查看上游参数和局部变量
仅看 #0 帧容易误判。必须往上翻至少 1–2 层,重点检查调用者是否传入了可疑指针:
-
bt full:显示每帧的局部变量值和参数,确认#0帧中出问题的指针变量(比如user、cfg)是否为0x0 -
f 1:切换到上一帧(即调用#0函数的那一层),再执行info args或直接print传入的实参,看它是不是从更上层来的 -
f 2:继续往上,直到找到第一个非系统库(如 libc、libpthread)的用户函数帧——那里大概率是逻辑源头 - 注意:如果
bt里出现大量??或地址无符号,说明调试信息缺失,需确保编译时加了-g且没 strip
结合 info registers 和 x/10i $pc 确认空指针来源
有时汇编级线索比 C 源码更直接。崩溃瞬间,寄存器里常存着“肇事指针”:
-
info registers:查看rax/rdi/rsi(x86_64)等通用寄存器值,若其中一个是0x0,再结合$pc(程序计数器)附近的指令,就能锁定哪条mov或call把它载入的 -
x/10i $pc:打印崩溃指令及前后几条汇编,找类似mov %rax,(%rdi)这种写操作,或callq *0x8(%rax)这种间接调用——%rax为 0 就必然崩 - 若崩溃在
__GI___pthread_kill或raise,说明是主动 abort,要查bt更早的帧里有没有assert、__builtin_trap或手动abort()
多线程下别忘了 info threads 和 thread N
空指针可能发生在子线程,而主线程早已退出或阻塞。只看默认线程的 bt 会漏掉关键现场:
-
info threads:列出所有线程及其状态,重点关注running或stopped但不在wait状态的线程 -
thread N:切换到目标线程(N 是 GDB 内部 ID,不是 PID),再执行bt full - 特别注意:如果某线程栈帧极浅(比如只有 2–3 层),且顶层是
start_thread或clone,说明它可能刚启动就因初始化失败而崩,要检查线程创建时传入的函数和参数
std::string::c_str() 返回空,但你没检查原始 std::string 是否为空),这时得结合源码和符号表交叉验证,而不是只信 bt 的表面顺序。











