应优先用 std::vector 替代裸数组以获得 at() 边界检查,用 std::array 替代固定大小栈数组,配合 ASan 检测运行时越界,避免自定义容器;遗留 C 接口需手动校验指针有效性。

用 std::vector 替代裸数组,自带边界检查
裸数组(T arr[N])在 C++ 中完全不检查下标,越界访问是未定义行为,编译器不会报错,运行时也常无声崩溃或数据错乱。而 std::vector 提供 at() 成员函数,会在越界时抛出 std::out_of_range 异常,便于定位问题。
-
vec.at(i)检查边界,vec[i]不检查——和裸数组一样快但不安全 - 调试阶段建议统一用
at();发布前若性能敏感,可保留[],但必须确保索引逻辑已验证 - 注意:
at()仅对有效vector对象起作用,空容器调用at(0)同样抛异常 - 示例:
std::vector<int> v = {1, 2, 3}; try { int x = v.at(5); // 抛 std::out_of_range } catch (const std::out_of_range& e) { // 处理越界 }
开启编译器地址 sanitizer(ASan),捕获运行时越界读写
静态分析和手动加 at() 都会漏掉指针算术、C 风格数组、堆内存操作等场景。ASan 是 Clang/GCC 内置的运行时检测工具,能精准报告越界地址、访问 size 为 0 的内存、释放后使用等问题。
- Clang/GCC 编译加
-fsanitize=address -g,链接时也需带上该 flag - ASan 会显著拖慢运行速度(2~3 倍)、增大内存占用,只用于开发/测试环境
- 它不检测栈上数组的越界读(部分版本支持,但不可靠),重点覆盖堆分配(
new/malloc)和全局数组 - 常见误报点:某些内联汇编、内存对齐填充、或自定义 allocator 可能触发假阳性,需结合
__asan_poison_memory_region排除
用 std::array 替代固定大小栈数组,获得编译期尺寸 + 运行时 at()
栈上 int buf[10] 看似安全,实则仍允许 buf[15] = 42 这类错误,且无任何提示。而 std::array 是零开销抽象,尺寸在编译期确定,同时提供与 vector 一致的 at() 接口。
-
std::array<int, 10> a;和int a[10];内存布局完全相同,无额外成本 -
a.at(i)在i >= 10时抛异常;a[i]仍不检查,慎用 - 不能用
std::array替换变长栈数组(如int buf[n];),后者是 C99 VLAs,C++ 标准不支持 - 配合 structured binding 使用更自然:
auto& [x, y, z] = std::array{1, 2, 3}; // 编译期检查元素数量
自定义容器包装裸数组?别费劲,优先用标准方案
有人想封装一个带检查的 SafeArray 类来兜底所有数组访问,实际效果往往不如直接用 std::vector 或 ASan。自己实现容易漏掉指针偏移、跨函数传递、模板推导等边界情况,还可能引入额外拷贝或破坏 ABI 兼容性。
立即学习“C++免费学习笔记(深入)”;
- 标准库容器已覆盖绝大多数场景:小数组用
std::array,动态用std::vector,关联查找用std::map/std::unordered_map - 遗留 C 接口传参必须用裸指针时,可在调用前用
assert(ptr && len > 0)+ 范围校验,而非包裹整个生命周期 - 真正难查的是多线程下因竞态导致的“伪越界”(如 A 线程刚释放内存,B 线程还没更新指针),这类问题 ASan 无法覆盖,得靠 TSan(ThreadSanitizer)
越界问题最麻烦的不是找不到工具,而是它常常不 crash、不报错、只悄悄改掉隔壁变量的值——结果在下游某个毫无关联的函数里突然表现异常。所以别只信“没崩就是没问题”,关键路径上至少要有一层 at() 或一次 ASan 测试跑过。










