应使用 std::vector 替代裸数组,日常开发优先调用 at() 方法进行带边界检查的下标访问,性能敏感场景可谨慎使用 operator[]。

用 std::vector 替代裸数组,下标访问前检查边界
裸数组(如 int arr[10])本身不提供越界检查,arr[15] = 42 是未定义行为,编译器不会报错,运行时可能静默覆盖内存或崩溃。安全修改的前提是让“下标是否合法”可判断、可拦截。
最直接的方案是改用 std::vector,它提供两种访问方式:operator[](不检查,和裸数组一样快但不安全)和 at()(带边界检查,越界抛出 std::out_of_range)。
- 日常开发中,只要不是性能敏感的内层循环,优先用
vec.at(i) = value - 如果确定索引已校验过(比如来自
for (size_t i = 0; i ),可用 <code>vec[i]省去检查开销 - 不要对
std::array抱有侥幸——它的at()虽然也检查,但尺寸固定,无法动态扩容,仍需手动确保索引在0到size()-1之间
裸数组必须用 std::size 和手动范围判断
若因兼容旧代码或嵌入式限制必须用裸数组,就不能依赖语言机制保安全,得靠人写防御逻辑。关键点是:不能硬编码长度,也不能只信传入的索引值。
- 用
std::size(arr)获取数组长度(C++17 起支持),避免sizeof(arr)/sizeof(arr[0])在函数参数退化为指针时失效 - 每次赋值前显式判断:
if (i - 注意:
i类型要是无符号(如size_t),否则i 的比较会因类型提升变成大数比较,导致误判
at() 抛异常后怎么处理才不算“崩溃”
很多人以为加了 at() 就万事大吉,结果程序遇到异常直接终止——因为没捕获。安全修改不等于“不崩溃”,而是“可控失败”。
- 在明确可能越界的场景(如用户输入索引、配置文件读取),必须用
try/catch包裹:try { vec.at(user_index) = new_value; } catch (const std::out_of_range& e) { // 记录日志、返回错误码、设默认值等 std::cerr - 不要在构造函数或析构函数里用
at()并忽略异常——C++ 中这些地方抛异常会导致std::terminate - 如果项目禁用异常,就别用
at(),老实用size()+ 手动判断
迭代器修改比下标更安全?不,只是换种风险
有人觉得用迭代器(如 vec.begin() + i)再解引用更“现代”,但这并不增加安全性——vec.begin() + i 越界仍是未定义行为,std::vector 不保证迭代器越界检查。
-
vec.begin() + i的合法性完全取决于i是否,和下标判断本质相同 - 使用
std::next(vec.begin(), i)也不会自动检查,且比+更慢 - 真正提升安全的是范围
for循环(for (auto& x : vec)),但它不适用于“按指定下标修改”的需求
下标安全的核心从来不是语法糖,而是把长度信息和索引校验绑在一起。哪怕用裸数组,只要每次修改前都走一遍 i ,就比盲目信任索引可靠得多。最容易被忽略的,是把“数组长度”当成魔法数字写死,或者在函数间传递裸数组时彻底丢失尺寸信息。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











