c++的try/catch无法捕获空指针解引用、数组越界、除零等未定义行为或信号错误,仅能捕获显式throw或标准库明确声明抛出的异常(如std::vector::at越界抛std::out_of_range)。

直接说结论:C++ 的 try/catch 不会自动捕获空指针解引用、数组越界、除零等运行时错误,这些属于未定义行为或信号(如 SIGSEGV),不是 C++ 异常,不能靠 catch(...) 捕获。
哪些错误能被 try/catch 捕获
C++ 异常机制只响应显式抛出的异常(throw)或标准库中明确声明会抛异常的函数(如 std::vector::at()、std::stoi()、new 失败时的 std::bad_alloc)。
-
std::vector::at(i)越界 → 抛std::out_of_range,可被catch(std::out_of_range&)捕获 -
std::stoi("abc")解析失败 → 抛std::invalid_argument -
throw std::runtime_error("oops")→ 可被catch(std::exception&)或更宽泛的catch(...)捕获 - 但
ptr->foo()中ptr == nullptr→ 触发 SIGSEGV,进程直接终止,catch(...)完全无效
为什么 catch(...) 看似“万能”却经常失效
catch(...) 只捕获 C++ 异常对象,不处理操作系统信号。在 Linux/macOS 下,段错误、总线错误、浮点异常等由信号机制管理,和 throw 无关。
- 想拦截 SIGSEGV?得用
signal()或sigaction()注册信号处理器,但这极其危险:信号上下文限制多,不能安全调用大多数 C++ 标准库函数(包括std::cout、new、throw) - 某些编译器(如 GCC)支持
-fnon-call-exceptions让某些硬件异常转为 C++ 异常,但这是非标准、不可移植的扩展,且仅对极少数情况生效(如 x86 上的某些除零),不推荐用于生产 -
catch(...)在栈展开(stack unwinding)过程中若又抛异常(比如析构函数里throw),程序直接调用std::terminate()—— 这是隐式规则,容易被忽略
正确使用 try/catch 的实操建议
把异常处理用在「可控的、语义明确的错误分支」上,而不是当“崩溃兜底”。
立即学习“C++免费学习笔记(深入)”;
- 优先用返回值或
std::optional/std::expected(C++23)表达可预期的失败,避免异常开销和栈展开不确定性 - 如果必须用异常,确保所有可能抛异常的函数都声明了
noexcept或明确异常规格(现代 C++ 推荐用noexcept显式标记不抛异常的函数) -
catch块中不要吞掉异常又不记录:至少写std::cerr ,否则调试时完全无迹可循 - 避免在构造函数初始化列表中调用可能抛异常的函数,除非你准备好处理
std::initializer_list构造失败导致对象未完成构建的情况
常见误用:用 try/catch 替代空指针检查
这是最典型的认知偏差。下面这段代码不会如预期工作:
int* p = nullptr;
try {
std::cout << *p << '\n'; // SIGSEGV,不是 C++ 异常
} catch(...) {
std::cout << "caught!\n"; // 永远不会执行
}
正确做法永远是主动防御:if (p != nullptr) { ... },或用 std::unique_ptr/std::shared_ptr 配合 operator bool() 检查;或者用 std::optional<t></t>(需注意引用包装限制)这类语义更清晰的工具。
真正难处理的,从来不是“怎么 catch”,而是“哪些根本就 throw 不出来”。











