【问题标题】:How to check Stack Usage when Calculating Ackermann计算 Ackermann 时如何检查堆栈使用情况
【发布时间】:2011-09-28 02:37:09
【问题描述】:

我正在了解我的系统计算阿克曼算法的两个和三个参数版本的能力。对于非常小的 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();
}

【问题讨论】:

  • 1.修复了标题拼写错误。 2. 重新标记 C++ -> C;这里没有 C++。 3. 用 POSIX 标记,因为这显然是您构建的 API。 (对于它的价值,阿克曼可能最好使用某种形式的动态编程而不是递归来计算)
  • 通过动态编程,您是否建议我尝试将 A0 和 A1 的返回值保存到表中?这样我就可以在进行另一个递归调用之前检查表格并为自己节省一些空间。
  • 是的,动态规划是一种表格驱动的算法求解方法。有关更多详细信息,请参阅我的答案中链接的维基百科文章。 AFAIK 你不应该需要超过m*n 的空间和时间来使用表格计算它。 (虽然我可能是错的;我自己没有实现它)考虑一个类似的情况,斐波那契数列,它需要递归计算的指数时间,但只需要使用动态规划计算的线性时间。

标签: c posix stack-overflow ackermann


【解决方案1】:

但是任何高于 3 或 4 的东西都不会返回并冻结我正在使用 atm 的终端。

这就是阿克曼函数的意义所在。它生长得非常迅速。对于m &gt;= 4n &gt;= 3,如果你递归地计算A(m, n),我怀疑你的函数会在你死之前返回。

我已经尝试了一些方法来捕获堆栈溢出,据我所知,c++ 没有我可以捕获的 stackoverflowexception。

当然不是。该进程的堆栈空间不足。应该立即拆除。

有没有更好的方法来检查与递归方法相关的堆栈使用情况?

如果您必须使用递归,请手动创建自己的堆栈数据结构,该结构分配在堆上而不是堆栈空间中。使用它来跟踪您在递归中的位置。 Push and pop and as you recursion,而不是通过嵌套方法调用进行递归。

但最后,无论如何,您都不应该使用递归来计算阿克曼。

【讨论】:

  • 你能详细说明为什么你不应该使用递归来计算阿克曼吗?我认为使用迭代循环无法完成。
【解决方案2】:

我已经尝试了一些方法来捕获堆栈溢出,据我所知,c++ 没有我可以捕获的 stackoverflowexception。 try-catch 块不起作用。在下面的代码中,我使用 getrlimit() 来查找堆栈限制,在主 gStackRef 中创建一个地址位置。我调用 checkStack 递归地检查指向 gStackLimit 的局部变量指针。

POSIX 没有检测堆栈溢出的“安全”方法。堆栈溢出导致SIGSEGV 信号,您(通常)不应捕获这些信号,因为它们也表示一般分段错误which should crash your program。 Windows 环境可以使用EXCEPTION_STACK_OVERFLOW 安全地处理堆栈溢出——但在这种情况下,Windows 所做的只是在堆栈末尾放置一个保护页并使用 SEH 通知。如果您用完保护页(在忽略 SEH 异常之后),那么您的程序将被终止(就像在 POSIX 领域一样)。

有没有更好的方法来检查与递归方法相关的堆栈使用情况?我还要检查段故障吗?我会让你知道我在一个 unix 终端上运行。

没有。即使你正在做的事情也有未定义的行为。在某些机器上,堆栈会增长。在某些机器上,堆栈会向下增长。编译器可以在两个方法之间插入任意数量的 slop space。从技术上讲,编译器可以实现这样的东西:有两个独立的堆栈,位于两个完全不同的内存段中,并且仍然是一致的。

如果您想以堆栈安全的方式计算 Ackermann,请使用从堆中分配的显式堆栈结构,或使用 dynamic programming

【讨论】:

    猜你喜欢
    • 2010-09-12
    • 2018-10-11
    • 2011-12-15
    • 2018-12-11
    • 2017-07-09
    • 1970-01-01
    • 2012-05-09
    • 2011-02-15
    • 2013-04-09
    相关资源
    最近更新 更多