C++中堆栈溢出无法用try/catch捕获,因其属操作系统级异常(如Linux的SIGSEGV或Windows的EXCEPTION_STACK_OVERFLOW),触发时程序已丧失正常执行上下文,C++异常机制无法介入;可靠做法是预防为主,包括限制递归深度、大对象堆分配、编译器栈保护及运行时栈限制检查。

堆栈溢出在C++中无法用try/catch捕获
直接说结论:std::set_terminate、std::set_unexpected 和 try/catch(...) 都**抓不到堆栈溢出(stack overflow)**。它属于操作系统级的异常(如 Linux 的 SIGSEGV 或 Windows 的 EXCEPTION_STACK_OVERFLOW),触发时程序已失去正常执行上下文,C++ 异常机制根本没机会介入。
Linux 下用 signal 捕获 SIGSEGV 并区分是否为栈溢出
Linux 中栈溢出通常表现为 SIGSEGV(而非 SIGSTKFLT,后者早已废弃)。但要注意:SIGSEGV 也可能是空指针解引用、非法地址访问等引起,必须结合 sigaltstack + sigaction 做初步甄别:
- 注册
SIGSEGV处理器时,务必设置SA_ONSTACK标志,否则处理函数自身可能再次触发栈溢出 - 在信号处理器中,检查
siginfo_t->si_code:若为SI_KERNEL且触发地址接近rlimit(RLIMIT_STACK)的上限,大概率是栈溢出 - 不要在信号处理器里调用
printf、malloc等非异步信号安全函数;可用write(2)输出简单提示
示例关键片段:
stack_t ss;
ss.ss_sp = malloc(SIGSTKSZ);
ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0;
sigaltstack(&ss, nullptr);
<p>struct sigaction sa;
sa.sa_sigaction = [](int, siginfo_t<em> si, void</em>) {
if (si->si_code == SI_KERNEL && (uintptr_t)si->si_addr < 0x7fff00000000ULL) {
write(STDERR_FILENO, "stack overflow detected\n", 24);
}
};
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigaction(SIGSEGV, &sa, nullptr);
Windows 下用 SetUnhandledExceptionFilter 捕获 EXCEPTION_STACK_OVERFLOW
Windows 提供明确的异常码 EXCEPTION_STACK_OVERFLOW(值为 0xC00000FD),可通过 SetUnhandledExceptionFilter 捕获:
立即学习“C++免费学习笔记(深入)”;
- 必须在主线程早期调用,且不能依赖 CRT 初始化后的设施(如
std::cout) - 处理函数中禁止调用任何可能分配栈空间的函数(包括大部分 STL 容器构造)
- 可安全使用
OutputDebugStringA或直接写入文件(需预先映射好内存/句柄) - 注意:捕获后无法“恢复执行”,只能记录日志、生成 minidump 后调用
ExitProcess
典型判断逻辑:
LONG WINAPI StackOverflowHandler(EXCEPTION_POINTERS* ep) {
if (ep->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
// 记录、dump、退出
ExitProcess(123);
}
return EXCEPTION_CONTINUE_SEARCH;
}
SetUnhandledExceptionFilter(StackOverflowHandler);
真正有效的防御手段不是捕获,而是预防和检测
靠事后捕获栈溢出属于被动兜底,风险高、可靠性低。更实际的做法是:
- 编译期加
-Wstack-protector(GCC/Clang)或启用 /GS(MSVC),让编译器插入栈保护检查 - 运行时用
getrlimit(RLIMIT_STACK, &rlim)获取当前栈限制,对深度递归函数手动计数并提前返回 - 将大对象(如大数组、大结构体)从栈移到堆(
std::vector、std::unique_ptr) - 用 AddressSanitizer(
-fsanitize=address)或 UBSan 检测栈溢出倾向(部分场景下能提前报错)
最易被忽略的一点:线程栈大小默认往往只有 1–8MB,创建新线程时若未显式指定 pthread_attr_setstacksize 或 std::thread 的栈尺寸参数,极易在递归或大局部变量场景下无声溢出。


















