C++ vector at函数会报错吗 C++数组越界异常捕获try-catch【纠错】

5次阅读

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

C++ vector at函数会报错吗 C++数组越界异常捕获try-catch【纠错】

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) { ... } 捕获,不能只 catch std::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 的检查不会被优化掉

有人担心开启 -O2at() 的边界检查会被编译器优化掉——不会。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,而是直接未定义行为。

text=ZqhQzanResources