如果不允许 LP 递归,那么可能会出现堆栈溢出的情况?

Posted

技术标签:

【中文标题】如果不允许 LP 递归,那么可能会出现堆栈溢出的情况?【英文标题】:If an LP recursion is not allowed, then there may be cases of stack overflow? 【发布时间】:2011-09-19 06:18:34 【问题描述】:

异常“堆栈溢出”是否仅与使用递归有关?在什么其他情况下我们可以给出这个例外?

【问题讨论】:

【参考方案1】:

需要注意的重要一点是,深度递归和深度调用链都从根本上不会导致堆栈溢出。造成溢出的原因是每次调用都会分配一个新的栈帧,从而增加了栈空间的使用。

许多语言通过使用尾调用优化允许任意深度递归/调用。一些语言,如 ML 和 Haskell 将在内部转换一些(当调用在调用函数结束时)函数/递归调用以避免使用额外的堆栈空间,从而有效地实现无限递归。这里的想法是,如果调用在调用函数的最后,调用函数的堆栈空间就不再需要,并且可以回收以供被调用函数使用。

【讨论】:

【参考方案2】:
    分配太大而无法放入堆栈的局部变量,例如在 64K 堆栈上包含一百万个元素的数组。 调用堆栈太深,即使没有递归,如果每个例程都有很多局部变量。例子: a() 调用 b() 调用 c()...调用 z() 调用 a1()...调用 z99()。 每个例程的局部变量以及每个函数的返回地址(可能还有一个堆栈粉碎保护器)都保留在堆栈上,直到每个函数退出时堆栈展开。

【讨论】:

【参考方案3】:

由于任何递归算法都可以转换为迭代算法,反之亦然,以下是 C99 中堆栈溢出的算法:

void ***()

    for (size_t n = 0; ; n *= 2)
        char a[n];

(关闭编译器优化。)

【讨论】:

【参考方案4】:

如果您在堆栈上分配的内存过多(例如,see in VS compiler)。

【讨论】:

【参考方案5】:

如果您使用 C99 的动态数组在堆栈上分配巨大的缓冲区。即

void stackkiller(int size) 
  char toohuge[size];
  printf("Don't call this with too big an argument!\n");

【讨论】:

嗯...有趣...动态数组版本导致我的系统出现分段错误,而静态数组版本导致“总线错误”。 哇,这不应该编译!编译器如何开始计算堆栈帧大小?它甚至以较小的 size 值运行的事实是一个意外,即使这样它也应该覆盖调用函数的堆栈。还是我走远了,编译器使用“合理”的大小值来计算堆栈帧大小? 如果您使用的是 C99,它会动态计算堆栈帧大小。就好像你调用了 alloca。

以上是关于如果不允许 LP 递归,那么可能会出现堆栈溢出的情况?的主要内容,如果未能解决你的问题,请参考以下文章

Python递归限制与堆栈大小?

堆栈溢出一般是由啥原因导致的?

如何解决栈溢出

预先在运行时检测堆栈溢出

如何启用递归函数以避免堆栈溢出?

怎么防止堆栈溢出