捕捉;“堆栈溢出”;递归C+中的异常+;功能 在递归C++函数中,是否可以捕获堆栈溢出异常< /代码>?如果是,怎么做

捕捉;“堆栈溢出”;递归C+中的异常+;功能 在递归C++函数中,是否可以捕获堆栈溢出异常< /代码>?如果是,怎么做,c++,exception,exception-handling,recursion,C++,Exception,Exception Handling,Recursion,那么在这种情况下会发生什么呢 void doWork() { try() { doWork(); } catch( ... ) { doWork(); } } 我不是在寻找特定操作系统的答案。一般来说大多数现代操作系统一直都在这样做。如果你想自己做这件事,你必须知道堆栈的最大“安全”地址(或者做一些数学运算来确定你可以安全调用函数的次数),但是如果你不自己管理调用堆栈,这可能会变得非常棘手,因为操作系统

那么在这种情况下会发生什么呢

void doWork()
{

     try() {

     doWork();    
     }


     catch( ... )  {

     doWork();    
     }
}  

我不是在寻找特定操作系统的答案。一般来说

大多数现代操作系统一直都在这样做。如果你想自己做这件事,你必须知道堆栈的最大“安全”地址(或者做一些数学运算来确定你可以安全调用函数的次数),但是如果你不自己管理调用堆栈,这可能会变得非常棘手,因为操作系统通常(出于充分的理由)会对你隐瞒这一点

如果您在内核空间中编程,这会变得非常简单,但我仍然怀疑您为什么要这样做。如果堆栈溢出,可能是因为错误的算法决定或代码中的错误


编辑:刚刚意识到您想要“捕获异常”结果。我认为我的答案根本不能直接回答这个问题(这个例外是否存在?我会想到一个惊人的失败),但我将把它留给洞察。如果您想删除它,请在注释中告诉我,我会这样做。

我怀疑是这样,当堆栈溢出时,程序甚至无法处理异常。通常操作系统会关闭该程序并报告错误。

这主要是因为无限递归。

在什么操作系统上?例如,您可以在Windows上使用结构化异常处理(或向量化异常处理)来执行此操作。不过,通常不能用本地C++异常处理来处理,如果这是你正在处理的。

<微软> C++ C++可以将结构化异常转换成C++异常。这是在VC++6中默认启用的。在较新的编译器中,默认情况下不会发生这种情况,但我非常确定,只要稍加探索,就可以重新启用它


确实,当这种情况发生时,堆栈空间不足。这就是我提到向量异常处理的部分原因。每个线程都有自己的堆栈,向量异常处理程序可以在引发异常的单独线程中运行。然而,即使是SEH,您也可以处理堆栈溢出异常——它只需要手动生成一个线程来完成大部分工作

真的没有便携式的方法。失控的递归函数在尝试分配堆栈地址空间以外的堆栈帧时,通常会导致无效的内存访问。根据操作系统的不同,这通常会导致程序崩溃,出现分段错误/访问冲突。换句话说,它不会抛出一个C++异常,它可以用语言的标准方式来处理。在Windows中,

可以使用结构化异常处理(SEH),用YORYTY和YO除了关键字安装自己的异常处理程序,可以捕获堆栈溢出、访问违规等。
避免Windows的默认崩溃对话框,并在需要时用自己的对话框替换它,这是非常好的方法。

您必须始终知道递归的级别,并检查它是否大于某个阈值。最大级别(阈值)由堆栈大小除以递归调用所需内存的比率计算得出


一次递归调用所需的内存是函数所有参数的内存加上所有局部变量的内存加上返回地址的内存+一些字节(大约4-8)。

当然,您可以通过将其转换为循环来避免递归问题

不确定您是否意识到这一点,但任何递归解决方案都可以转换为基于循环的解决方案,反之亦然。通常希望使用基于循环的解决方案,因为它更易于阅读和理解


无论使用递归还是循环,都需要确保退出条件定义良好,并且始终会被命中。

没有可移植的方法。然而,有一些不可移植的解决方案

首先,正如其他人所提到的,Windows提供了一个非标准的
\uu try
\uu,除了名为(在知识库中)的
框架之外

其次,
alloca
——如果实现正确——可以告诉您堆栈是否即将溢出:

bool probe_stack(size_t needed_stack_frame_size)
{
    return NULL != alloca(needed_stack_frame_size);
};
我喜欢这种方法,因为在
probe_stack
的末尾,分配的内存
alloca
被释放并可供您使用。不幸的是,只有少数操作系统正确地实现了alloca<在大多数操作系统上,code>alloca
从不返回
NULL
,让您发现堆栈已溢出,并出现了惊人的崩溃

第三,类UNIX系统通常有一个用函数调用的头来设置堆栈的大小(或者,实际上,将多个堆栈链接在一起)。您可以跟踪堆栈上的位置,并确定是否即将溢出。Windows也有类似的功能



从Windows 8开始,Windows有一个()

即使您可以像在Windows中那样以不可移植的方式执行此操作,这仍然是一个非常糟糕的主意。最好的策略是首先不要使堆栈溢出。如果您需要与不受控制的代码隔离,请在不同的进程中运行该代码,这样您就可以检测到它何时崩溃。但你不想在你自己的过程中做这种事情,因为你不知道违规代码会对国家造成什么样的严重腐败,这会让你变得不稳定


微软的Raymond Chen提出了一个有趣的、有点相关的问题,关于为什么不应该在Windows上的用户模式应用程序中检查有效指针。

这本身并不是一个例外,但如果您只想将堆栈使用限制在一个固定的数量,您可以这样做:

#include <stdio.h>

// These will be set at the top of main()
static char * _topOfStack;
static int _maxAllowedStackUsage;

int GetCurrentStackSize()
{
   char localVar;
   int curStackSize = (&localVar)-_topOfStack;
   if (curStackSize < 0) curStackSize = -curStackSize;  // in case the stack is growing down
   return curStackSize;
}

void MyRecursiveFunction()
{
   int curStackSize = GetCurrentStackSize();
   printf("MyRecursiveFunction:  curStackSize=%i\n", curStackSize);

   if (curStackSize < _maxAllowedStackUsage) MyRecursiveFunction();
   else
   {
      printf("    Can't recurse any more, the stack is too big!\n");
   }
}

int main(int, char **)
{
   char topOfStack;
   _topOfStack = &topOfStack;
   _maxAllowedStackUsage = 4096;  // or whatever amount you feel comfortable allowing

   MyRecursiveFunction();
   return 0;
}
#包括
//这些将设置在main()的顶部
静态字符*_topOfStack;
静态int_maxAllowedStackUsage;
int GetCurrentStackSize()
{
charlocalvar;
int
If you use Visual C++
Goto C/C++ , Code Generation
Choose "Both..." in "Basic Runtime Checks"