我正在了解我的系统能够计算Ackermann算法的两个和三个参数版本。对于非常小的m和n值,我的系统将计算并打印从A0和A1方法调用返回的结果。然而,高于3或4的任何东西都不会返回并冻结我正在使用atm的终端。我的问题是我确定我的机器可以计算的m和n的值。
我已经尝试了一些事情来捕获堆栈溢出,因为我知道c ++没有stackoverflowexception我可以捕获。 try-catch块不起作用。在下面的代码中,我使用getrlimit()来查找堆栈限制,在主gStackRef中创建一个地址位置。我调用checkStack以递归方式检查指向gStackLimit的局部变量指针。
有没有更好的方法来检查与递归方法相关的堆栈使用情况?我也检查段故障?我会告诉你我在unix终端上运行。
#include <cstdlib>
#include <iostream>
#define _XOPEN_SOURCE_EXTENDED 1
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlp);
using namespace std;
int * gStackRef;
int gStackLimit;
void checkStack(void);
int main(int argc, char *argv[])
{
int temp = 0;
gStackRef = &temp;
rlimit myl;
getrlimit(RLIMIT_STACK, &myl);
gStackLimit = (myl.rlim_cur / 3 * 8 / 10) ;/* modified for segment fault */
cout << gStackLimit << "\n";
checkStack();
}
void checkStack()
{
int temp = 0;
int* pVariableHere = &temp;
size_t stackUsage = gStackRef - pVariableHere;
printf("Stack usage: %d / %d \n", stackUsage, gStackLimit);
if(stackUsage > gStackLimit) return;
else checkStack();
}
答案 0 :(得分:2)
然而,任何高于3或4的东西都不会返回并冻结我正在使用atm的终端。
这就是Ackermann功能的重点。它发展得非常迅速。对于m >= 4
和n >= 3
,如果您以递归方式计算A(m, n)
,我会怀疑您的函数会在您死亡之前返回。
我已经尝试了一些事情来捕获堆栈溢出,因为我知道c ++没有我可以捕获的stackoverflowexception。
当然不是。该过程超出了堆栈空间。它应该立即拆除。
有没有更好的方法来检查与递归方法相关的堆栈使用情况?
如果必须使用递归,请通过创建自己的堆栈数据结构来手动执行,该堆栈数据结构在堆上而不是在堆栈空间中分配。用它来跟踪你在递归中的位置。推送和弹出,然后递归,而不是通过嵌套方法调用递归。
但最后,你不应该使用递归来计算Ackermann。
答案 1 :(得分:0)
我已经尝试了一些事情来捕获堆栈溢出,因为我知道c ++没有stackoverflowexception我可以捕获。 try-catch块不起作用。在下面的代码中,我使用getrlimit()来查找堆栈限制,在主gStackRef中创建一个地址位置。我调用checkStack以递归方式检查指向gStackLimit的局部变量指针。
POSIX没有检测堆栈溢出的“安全”方法。堆栈溢出导致SIGSEGV
信号,您(通常)不应该捕获这些信号,因为它们也表示一般的分段错误which should crash your program。 Windows环境可以使用EXCEPTION_STACK_OVERFLOW
安全地处理堆栈溢出 - 但在这种情况下,Windows正在做的只是在堆栈末尾放置一个保护页面并通知SEH。如果你用完了保护页面(在忽略SEH例外之后),那么你的程序就会终止(就像在POSIX-land中一样)。
有没有更好的方法来检查与递归方法相关的堆栈使用情况?我也检查段故障?我会告诉你我在unix终端上运行。
没有。即使你正在做的事情也有不明确的行为。在某些机器上,堆栈会增长。在某些机器上,堆栈会逐渐减少。编译器可以在两个方法之间插入任意数量的slop空间。从技术上讲,编译器可以实现两个独立的堆栈,位于两个完全不同的内存段中,并且仍然符合要求。
如果要以堆栈安全方式计算Ackermann,请使用从堆分配的显式堆栈结构,或使用dynamic programming。