段错误是运行时非法内存访问所致,需用gdb回溯调用栈和AddressSanitizer定位越界或释放后使用问题,配合调试编译与容器安全用法可高效排查。

段错误发生时,先别急着改代码
段错误(Segmentation fault)不是语法错误,而是运行时访问了非法内存——比如解引用空指针、访问已释放的堆内存、栈溢出、数组越界写入只读段等。它不报具体哪行错,只给个信号(SIGSEGV),所以得靠工具定位。
常见现象:./a.out 直接退出,终端只显示 Segmentation fault (core dumped);用 gdb 启动却停在 ?? 地址;加了 -O2 编译后才出错,关优化反而正常。
- 必须用带调试信息编译:
g++ -g -O0(关优化才能对齐源码行) - 不要依赖
cout或printf打印“定位”,它们可能被缓冲,崩溃前根本没输出 - 如果程序跑一会儿才崩,优先怀疑野指针或堆损坏,不是第一处
new/delete,而是后续某次内存操作触发检查
用 gdb 看清崩溃那一瞬间
gdb 是最直接的手段,重点不是“怎么启动”,而是“崩溃后怎么看”。
- 启动:
gdb ./a.out,然后run(或run arg1 arg2) - 崩溃后立刻输
bt(backtrace),看调用栈——注意最上面几帧,尤其是你自己的函数名 - 输
frame 0再list,能显示崩溃点附近源码;print 变量名查值,比如print ptr看是不是0x0 - 如果
bt显示??或地址乱码,说明没编译调试信息,或用了strip,重编译
示例:崩溃在 std::vector::operator[] 里?那大概率是下标越界,检查前面传进去的 i 值是否 = vec.size()。
立即学习“C++免费学习笔记(深入)”;
AddressSanitizer 比 gdb 更早发现越界和释放后使用
很多段错误源于 UAF(Use After Free)或缓冲区溢出,gdb 只能看到“爆了”,但 asan 能告诉你“谁写的、哪行写的、当时内存长啥样”。
- 编译加
-fsanitize=address -g,例如:g++ -fsanitize=address -g main.cpp - 运行时报错会直接指出:
heap-use-after-free、stack-buffer-overflow、甚至打印出分配/释放的完整调用栈 - 注意:
asan会显著变慢、吃内存,只用于开发调试,别上生产 - 配合
export ASAN_OPTIONS=detect_stack_use_after_return=1还能抓到栈上变量返回后访问(比如返回局部数组指针)
它不替代 gdb,但能帮你把“不确定哪错了”变成“明确知道哪行越界了”。
std::string 和 std::vector 的隐式陷阱
现代 C++ 容器本身不会段错误,但它们的用法会——尤其混用 C 风格接口或忽略边界检查。
-
str.c_str()返回的指针在str被移动、赋值、销毁后立即失效,别存起来反复用 -
vec.data()和&vec[0]在vec.empty()时解引用必崩,用前必须判空 -
std::vector::at()会抛std::out_of_range,而operator[]不检查——线上用[]要自己兜底,调试期建议临时换成at() - 用
std::string_view接收参数时,确保原始std::string生命周期比string_view长,否则就是悬垂视图
这些不是语言 bug,是 RAII 和所有权模型下的合理约束——违反它,段错误只是时间问题。
真正难的不是找到崩溃点,而是理解“为什么这段合法代码会访问非法地址”。内存生命周期、对象析构顺序、多线程竞争、ABI 不兼容的库混用……这些不会报错信息,只能靠经验+工具交叉验证。











