gdb可直接定位数组越界段错误:先-g编译,运行gdb后run复现崩溃,用bt查看调用栈定位源码行号,再用info registers或x/10i $rip检查非法地址指令;配合asan(-fsanitize=address)能精准报出越界位置、字节数及内存对象类型。

用 gdb 直接定位数组越界位置
段错误发生时,gdb 是最直接的排查工具。先用 -g 编译:
g++ -g -o test test.cpp,再运行 gdb ./test,输入 run 复现崩溃。程序中断后,执行 bt(backtrace)看调用栈,重点找最后一层你的源码行号;再用 info registers 或 x/10i $rip 确认出错指令,通常能看到类似 movl %eax,(%rdx) 这种写入操作——如果 %rdx 的值是非法地址(比如 0x0、极大值或未映射页),基本就是数组下标算错了。
静态检查:-fsanitize=address 比 valgrind 更快更准
ASan 在编译期插桩,能精准报出数组越界的读/写位置、越界多少字节、以及访问的是哪个栈/堆对象。启用方式很简单:g++ -fsanitize=address -g test.cpp -o test,直接运行即可。它比 valgrind --tool=memcheck 启动快、误报少,尤其对栈上数组(如 int a[10])越界访问识别非常可靠。注意两点:
- 必须加
-g,否则只报地址不报行号 - 不能和
-O2以上混用,优化可能移除边界检查逻辑
std::vector::at() 替代 [] 捕获越界访问
如果你控制得了代码修改,把裸数组换成 std::vector,并用 .at() 访问:
v.at(i) 会在运行时检查 i >= v.size(),抛出 std::out_of_range 异常,而不是静默越界写内存。这招适合调试阶段快速暴露问题,但要注意:
-
.at()有函数调用开销,上线前可切回[](前提是已确认安全) - 它只检查下标,不防负数索引(
size_t类型会把负数转成极大正数,仍会越界) - 对原始指针(如
new int[10])无效,必须是vector对象
常见陷阱:循环变量类型和无符号溢出
段错误常藏在看似“合法”的循环里。比如:for (size_t i = vec.size(); i >= 0; --i)——size_t 是无符号类型,i-- 到 0 后继续减,会绕成极大正数(如 18446744073709551615),接着访问 vec[i] 必然崩。这类问题 gdb 只显示非法地址,但看不出是循环逻辑错。解决办法:
- 循环变量改用有符号类型(如
int i),或重写为for (size_t i = 0; i - 用
-Wsign-compare编译选项让编译器警告无符号/有符号比较 - ASan 对这种越界也能捕获,但需确保循环体里真有数组访问,否则只报地址不报上下文
真正难查的不是越界本身,而是越界后没立刻崩溃——比如写到了相邻栈变量的内存里,改掉了别的局部变量值,几层函数之后才出问题。这时候 ASan 和 gdb 配合看访问地址和栈帧,比单靠日志或猜更靠谱。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











