std::vector::at() 会检查下标越界并抛出 std::out_of_range 异常,而 operator[] 不检查,越界访问属未定义行为;asan 是最有效的运行时越界检测手段,可精确定位问题源头。

std::vector::at() 会检查下标,但 [] 不会
直接用 v[i] 访问 std::vector 或 std::array 时,编译器不做任何检查——哪怕 i == v.size() 或负数,也属于未定义行为。而 v.at(i) 在所有标准库实现中(包括 Release 模式)都会判断 i >= 0 && i ,越界就抛 <code>std::out_of_range。
常见错误现象:v[5] 访问 size=5 的 vector,看似读到垃圾值或“刚好没崩”,实则已破坏内存布局,后续行为不可预测。
- 仅对
std::vector、std::array、std::string、std::deque有效;对裸指针、C 风格数组完全无效 - 必须配
try/catch,否则异常未捕获会导致进程终止 - 每次调用有分支开销,高频循环(如每帧上万次)中需权衡
裸数组或栈数组只能靠手动检查 if (i >= 0 && i
C 风格数组(int arr[10])、std::array 的 []、结构体内嵌数组,都没有运行时边界保障。唯一可靠方式是显式比较索引和长度。
使用场景:性能敏感路径、嵌入式环境、无法改用容器的遗留代码。
- 长度
N必须是确定值(宏、constexpr、函数参数传入),不能是运行时才知的变量 - 注意符号问题:用
size_t作索引时,i 足够(<code>size_t无负值),但i >= 0变成冗余且可能触发编译器警告 - 别把检查逻辑写在宏里再反复展开——容易漏改,建议封装为
safe_access(arr, i, N)函数模板
AddressSanitizer 编译期插桩,能捕获所有越界访问
加 -fsanitize=address 编译后,任何越界读写(堆、栈、全局区)都会在运行时立即报错,附带源码行号、内存地址、访问类型(READ/WRITE)和调用栈。
这是目前最省心的兜底方案,尤其适合 CI 测试和开发阶段快速暴露问题。
- 仅 GCC/Clang 支持;MSVC 用户得换用
/RTC1,但只覆盖栈数组,功能弱很多 - 必须关优化(
-O0或-O1),否则某些越界会被编译器优化掉而漏报 - 性能开销约 2×,内存占用翻倍,绝不能上线启用
- 对“跨相邻 malloc 块”的擦边越界可能不报——ASan 依赖 redzone,不是全量监控
自定义 SafeArray 类适合长期维护的关键模块
当既要检查、又不能依赖 ASan,还无法统一替换为 std::vector(比如封装硬件寄存器映射数组),可写一个轻量 wrapper。
核心点:检查逻辑必须每次访问都执行,且不能被编译器优化掉(尤其 Release 下)。
- 模板参数
N得是编译期常量,才能做static_assert和避免分支 - 用
[[nodiscard]]标记返回值,防用户忽略检查结果 - 异常处理要收敛:要么抛
std::out_of_range,要么__builtin_trap()终止,别用assert()(Release 下消失) - 别试图拦截
p + 10这类指针算术——cppcheck 的pointerOutOfBounds才管这个,运行时成本太高
at() 适合业务逻辑层快速兜底,ASan 是构建流水线里的必过门禁,而手动检查或 SafeArray 往往出现在驱动、协议解析、图形管线等对内存布局零容忍的位置。漏掉任意一层,都可能让 bug 潜伏数月。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











