【问题标题】:Why doesn't the item in the stack gets popped off immediately when you return in recursion?为什么递归返回时堆栈中的项目不会立即弹出?
【发布时间】:2019-11-04 19:32:14
【问题描述】:

好吧,这可能是个愚蠢的问题。但我认为每当你返回一些东西时,堆栈中的项目就会被弹出。但是在递归的情况下,堆栈中的项目会不断堆积,直到达到基本情况。然后它开始弹出。我想知道为什么会这样。

还有,一个项目究竟是什么时候从堆栈中弹出的?

谢谢

【问题讨论】:

  • 究竟是哪个项?当前堆栈帧确实在返回时从堆栈中弹出,即使在递归函数调用中也是如此。
  • 确实如此。但是你在返回之前进行递归调用——这就是递归的原因。

标签: recursion stack


【解决方案1】:

例如:

def bar(n):
    if n > 0:
        print(n)
        return bar(n-1)
    return 'end'

return bar(n-1)语句中,我们先执行bar(n-1),然后返回the return valuebar(n-1)

当我们执行bar(n-1) 时,我们会遇到类似的情况,除非n > 0 不再是True

这就是我们可以进行递归的原因。

也许我的解释很无聊,我想分享一个visualized link你很想看看

【讨论】:

    【解决方案2】:

    我将假设“item”,您指的是函数 call stack 的堆栈框架,而不是 stack data structure 中的 item 元素(其操作类似于调用堆栈)。我希望澄清有关调用堆栈如何工作的困惑。

    还要注意process 中的每个thread 都有自己的调用堆栈。现在,假设我们正在使用一个具有一个正在运行的执行线程的进程。


    递归函数是调用自身的函数。每当调用函数时,到达函数调用指令的线程都会创建一个堆栈帧。堆栈帧是一块内存,包含执行被调用函数所需的所有数据(变量、参数等)。此帧被“推”到调用堆栈的顶部。当前正在执行的调用函数被暂时挂起,堆栈指针向上移动到新的帧,然后执行。

    没有基本情况或函数不再进行递归调用的条件状态,递归将无限期地继续。发生这种情况时,操作系统会在进程超出其堆栈使用量(通常约为 1 MB)时发送信号以终止进程。

    在达到基本情况并执行函数中的最后一行代码后,线程开始从上到下解析堆栈帧,并在它们完成执行时将它们从堆栈中弹出(它们可能会做更多的工作或进行额外的递归电话)。

    pop 相当于释放与帧相关的内存、处理返回值、将堆栈指针移回调用者并在进行调用的位置恢复执行。

    这是一个用 C 和 Python 编写的示例程序,其中包含一个递归函数和一个图表:

    #include <stdio.h>
    
    void count_to(int n) {
        if (n > 0) {
            count_to(n - 1);
        }
    
        printf("%d\n", n);
    }
    
    int main() {
        count_to(4);
        return 0;
    }
    
    def count_to(n):
        if n > 0:
            count_to(n - 1)
    
        print(n)
    
    count_to(4)
    

    这是程序的输出:

    0
    1
    2
    3
    4
    

    这是执行期间调用堆栈的样子:

    [count_to(4)]  <-- top of call stack/stack pointer
    [main]         <-- bottom of call stack
    
    [count_to(3)]  <-- 
    [count_to(4)]  
    [main]         
    
    [count_to(3)]  <--
    [count_to(4)]
    [main]         
    
    [count_to(2)]  <-- 
    [count_to(3)]
    [count_to(4)]
    [main]         
    
    [count_to(1)]  <--
    [count_to(2)]
    [count_to(3)]
    [count_to(4)]
    [main]         
    
    [count_to(0)]  <--
    [count_to(1)]
    [count_to(2)]
    [count_to(3)]
    [count_to(4)]
    [main]         
    

    此时,尚未打印任何内容。该线程刚刚将函数调用添加到堆栈中。但是一旦我们调用count_to(0),递归就会停止。当我们弹出堆栈帧时,每个函数将打印其本地值n 并返回给它的调用者。

    print(0)
    return
    
    [count_to(1)]  <-- 
    [count_to(2)]
    [count_to(3)]
    [count_to(4)]
    [main]         
    
    print(1)
    return
    
    [count_to(2)]  <-- 
    [count_to(3)]
    [count_to(4)]
    [main]         
    
    print(2)
    return
    
    [count_to(3)]  <--
    [count_to(4)]
    [main]         
    
    print(3)
    return
    
    [count_to(4)]  <--
    [main]         
    
    print(4)
    return
    
    [main]         <--
    

    最后,执行返回到main,然后退出。


    我建议阅读维基百科的文章,例如 stack-based memory allocationcall stack,并编写很多微型递归程序,例如这里的示例,看看它们是如何工作的。

    【讨论】:

      猜你喜欢
      • 2014-04-22
      • 2022-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-21
      相关资源
      最近更新 更多