vector::at 会抛出 std::out_of_range 异常而非静默越界,下标超出 size() 时严格保证异常抛出,与 operator[] 的未定义行为有本质区别,必须用 try-catch 捕获且不可依赖 errno 或返回值。

vector::at 会抛出异常而不是静默越界
vector::at 在下标超出 size() 范围时,一定会抛出 std::out_of_range 异常,不是崩溃也不是未定义行为。这和 operator[] 的行为有本质区别——后者不检查边界,越界访问直接导致未定义行为(可能 crash、数据错乱或看似正常)。
常见错误现象:用 at(i) 替代 operator[](i) 后程序突然终止,但没看到 catch 到异常,其实是没写 try-catch 或异常被上层吞掉了。
- 必须用
try { vec.at(i); } catch (const std::out_of_range& e) { ... }捕获,不能只 catchstd::exception(虽然它能捕获,但不够精准) -
at()的性能略低于operator[],因为每次调用都做一次if (i >= size())判断 - 空
vector调用at(0)也会抛std::out_of_range,不是空指针或 nullptr 相关错误
std::out_of_range 异常不能用常规 C 风格错误码捕获
试图用 if (errno != 0) 或检查返回值来“捕获” at() 的越界是无效的——C++ 异常机制和 errno 完全无关,errno 不会被设置,函数也无返回码可查。
典型误用场景:在 C++ 项目里混用 C 风格错误处理逻辑,比如:
立即学习“C++免费学习笔记(深入)”;
int x = vec.at(i); // 如果抛异常,下面这行根本不会执行
if (errno) { ... } // 永远进不去
-
std::out_of_range继承自std::exception,只能用catch捕获 - 某些编译器/标准库实现中,
what()返回字符串类似"vector::_M_range_check: __n (which is X) >= this->size() (which is Y)" - 不要依赖
what()字符串内容做逻辑判断,它只是调试信息
数组原生类型(如 int arr[5])没有 at 函数,越界完全无法捕获
C++ 原生数组(int arr[10])不提供 at 或任何边界检查,arr[15] 是纯未定义行为,编译器不报错、运行时不抛异常、try-catch 完全无效。
常见误解:以为给原生数组加了 try-catch 就能兜住越界——实际连异常都不会抛,更不会进入 catch 块。
- 想获得安全访问,必须改用
std::array(有at())或std::vector -
std::array::at行为与vector::at一致,越界抛std::out_of_range - 用
g++ -fsanitize=address可以在调试期检测原生数组越界,但这是 ASan 工具行为,不是语言特性
release 模式下 at 的检查不会被优化掉
有人担心开启 -O2 后 at() 的边界检查会被编译器优化掉——不会。at() 的语义明确要求越界时抛异常,标准强制要求该检查存在,所有主流标准库(libstdc++、libc++、MSVC STL)都保留它。
但要注意:如果编译器能**静态证明**下标一定合法(比如 vec.at(3) 且 vec.size() 在编译期已知 ≥4),部分优化器可能省略运行时判断——但这属于安全优化,不影响正确性。
- 别为了性能盲目换回
operator[],先确认这里真是性能瓶颈(profile 数据说话) - 若确实要禁用检查,可用
data()[i](仍需确保 i 在范围内),但失去异常保护 - Release 下异常本身开销比检查更大;如果确定不会越界,
operator[]是更轻量的选择
at() 抛异常的前提是「访问发生在 vector 生命周期内」。如果 vector 已被 move 或析构,再调用 at() 就是野指针访问,不会抛 std::out_of_range,而是直接未定义行为。










