gdb可实时捕获段错误并精确定位源码行号:编译加-g,运行后崩即停,用bt查看调用栈,90%问题源于nullptr解引用、数组越界或野指针。

用gdb直接看崩溃时的调用栈
段错误发生时,程序已中断在出错位置,gdb 能立刻告诉你哪一行访问了非法内存。关键不是等它崩完再猜,而是让程序在崩的瞬间停住。
- 编译时务必加
-g:比如g++ -g -o main main.cpp,否则gdb只能显示汇编,看不到源码行号 - 运行:
gdb ./main,然后输入run(或简写r),复现崩溃 - 崩溃后立刻输
bt(backtrace),输出类似:#0 0x00005555555547a2 in foo() at main.cpp:12 #1 0x00005555555547d5 in main() at main.cpp:20
——第 12 行就是问题源头 - 如果
bt显示??或地址而非函数名,说明没加-g,或者用了-O2优化干扰了调试信息,先关优化重编
检查常见非法内存操作模式
90% 的段错误来自这几类写法,定位到行号后,盯住这一行及上下文变量状态即可快速确认:
-
nullptr解引用:比如ptr->value前没判if (ptr == nullptr) - 数组/容器越界:
arr[10]但arr只有 10 个元素(合法下标是 0–9);vector用operator[]不检查边界,改用at()可抛异常辅助定位 - 使用已
delete或已离开作用域的指针:int* p = new int(42); delete p; cout —— 此时 <code>p成悬垂指针 - 栈溢出:递归过深、超大局部数组(如
char buf[1024*1024];),gdb的bt会显示极长的重复调用帧
用 AddressSanitizer 在运行时自动报错
比 gdb 更早发现问题,尤其适合难以稳定复现或偶发的段错误。它会在非法访问发生时立即打印详细错误类型和代码行,不依赖手动断点。
- 编译加
-fsanitize=address -g:例如g++ -fsanitize=address -g -o main main.cpp - 直接运行
./main,一旦触发越界或 use-after-free,会输出类似:================================================================= ==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000010 at pc 0x5555555547a2 bp 0x7fffffffe0a0 sp 0x7fffffffe098 READ of size 4 at 0x602000000010 thread T0 #0 0x5555555547a2 in foo() /path/main.cpp:12 - 注意:ASan 会显著拖慢运行速度、增大内存占用,仅用于开发调试,不要带它上线
- 不支持所有平台(如某些嵌入式环境),但主流 Linux/macOS/x86_64 完全可用
为什么有时候 gdb 显示的行号不准?
不是 gdb 错了,而是编译器优化或内联把逻辑“揉”在一起了。最典型的是 std::vector::operator[] 越界——实际崩溃在 vector 内部的指针解引用,但真正该负责的是你调用它的那行。
- 看
bt最顶上几帧,重点读倒数第二、第三层:顶层是库内部(如__gnu_cxx::__normal_iterator),往下找第一个属于你源文件的帧 - 如果所有帧都显示
??,确认是否漏了-g,或用了-fomit-frame-pointer(某些发行版默认开启),加-fno-omit-frame-pointer可修复回溯 - 内联函数(
inline或编译器自动内联)会让多行逻辑压缩成一行汇编,此时list命令在gdb里看附近源码比单看行号更有用
段错误本身不保存上下文,靠的是调试信息和运行时检测工具。没有 -g 就像修车不带电路图,没有 ASan 就像只靠听异响判断故障——两者配合用,才能稳准快。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











