【问题标题】:What is the relationship between recursion functions and memory stack?递归函数和内存栈有什么关系?
【发布时间】:2012-11-30 17:56:54
【问题描述】:

递归函数和内存堆栈之间是否存在直接关系,更多解释请考虑代码:

public static int triangle(int n) {
    System.out.println(“Entering: n = ” + n);
    if (n == 1) {
        System.out.println(“Returning 1”);
        return 1;
    } else {
        int temp = n + triangle(n - 1);
        System.out.println(“Returning“ + temp);
        return temp;
    }
}​

在此示例中,值 2、3、4、5 将存储在哪里,直到函数返回?请注意,它们将在 LIFO(LastInFirstOut) 中返回,这些是处理内存堆栈的递归的特殊情况,还是它们总是一起出现?

【问题讨论】:

  • 如果您指的是调用堆栈,那么每次调用都会在调用堆栈上创建一个条目,并且每次返回都会删除该条目。
  • 你从哪里得到那些在 Java 中实际上不起作用的花哨的引语?
  • 如您所见,n==1 是特例,不会递归。

标签: recursion stack


【解决方案1】:

是的,递归函数和内存堆栈之间存在直接关系,因为某些具有上限的函数会因为达到堆栈大小限制而使您的程序崩溃,并且函数将覆盖您的部分程序代码(这就是我们所说的堆栈-溢出)。

R:递归

我:迭代

first call:
 R  |  I
|_|   |_|

second call:
 R  |  I
|_|   |_|
|_|

third call:
 R  |  I
|_|   |_|
|_|
|_|
.
.
.
n call :
 R  |  I
|_|   |_|
|_|
|_|
.
.
.
|_| 

我希望这是有道理的,因为迭代调用函数一旦完成就会被压入堆栈,它会从堆栈中退出,下一次调用将加载一个类似的函数,另一方面,递归函数会加载到堆栈中并调用自身并在每次调用时重新加载堆栈,然后在达到停止条件时开始关闭(LIFO 最后一个调用第一个输出)。

所以现在具体到您的问题,当满足停止条件时,您所说的 n 值将保存在内存中,然后最后一个函数将显示 n,然后退出以将手交给刚刚调用的函数它也将显示它自己的 n 值,并且将重复相同的事情,直到调用第一个函数,但是迭代函数将显示计数器 n 的值(仅使用一个变量,我们正在更改它的值)。

下面是一篇关于stackoverflow的好文章,

非常深或无限递归主条目:无限递归 堆栈溢出的最常见原因是过深或无限 递归。像 Scheme 这样实现尾调用的语言 优化,允许特定排序的无限递归——尾 递归——在没有堆栈溢出的情况下发生。这有效,因为 尾递归调用不占用额外的堆栈空间。

http://en.wikipedia.org/wiki/Stack_overflow

【讨论】:

    【解决方案2】:

    假设这是一个 C++ 类方法,对 triangle(n) 的调用会将数据推送到堆栈,如下所示:

    • function code
    • int *returnAddress
    • int n

    在函数返回之前不会分配返回值。 R.S. Shaw 为Call Stack wikipedia pagehere 提供了一个很好的图像示例。

    每次递归时,此数据都会被推送到调用堆栈的顶部,因此最后一次调用 triangle(n) 的代码将位于顶部。存储在*returnAddress 中的值是内存中结果需要去的地方,以便展开递归。

    换句话说,结果本身(例如:三角形 (1) 为 1,三角形 (2) 为 3)最终位于堆栈的 function code 部分的某个位置,而不是内存中特定命名的位置。如果您运行调试器,您应该能够通过在 triangle 函数代码中放置一个断点来跟踪您的 returnAddress 的位置。

    顺便说一句,这不是递归的特殊情况。这是经典的教科书案例。

    【讨论】:

      【解决方案3】:

      临时变量 n 将在堆栈上,调用 n-1 的参数和返回地址都将在堆栈上。这些都在同一个堆栈上。

      【讨论】:

        【解决方案4】:

        正如 QuentinQK 所解释的,局部变量 n 将消耗堆栈空间以及函数的返回地址,并且 - 在短时间内 - 返回值和所有移交给递归函数的参数都会消耗堆栈空间。这发生在每个递归级别。因此,这取决于您的递归将进行多深(该函数调用自身的频率)最终将需要多少堆栈以及它是否会爆裂。

        出于这个原因,必须有 sim final 条件。在你的例子中,它是这个if (n==1)' condition where the recursion is stopped when that value is reached. It only works along with then-1in the parameter list of the recursive call oftriangle`。

        但你真正想知道什么?

        【讨论】:

          猜你喜欢
          • 2012-06-13
          • 2018-06-10
          • 2023-03-27
          • 1970-01-01
          • 2019-10-18
          • 2016-12-28
          • 2011-06-16
          • 1970-01-01
          • 2018-09-24
          相关资源
          最近更新 更多