在Recursion factorial程序中,当函数返回如何维护堆栈帧时?

时间:2015-01-14 03:27:02

标签: recursion

例如:功能实施:

 facto(x){
     if(x==1){ 
             return 1;
      }
      else{ 
           return x*facto(x-1); 
     }

以更简单的方式让我们采取堆栈 - >

        returns
 |2(1)|---->    2(1) evaluates to 2
 |3(2)|---->    3(2)<______________| evaluates to 6
 |4(3)|---->    4(6)<______________| evaluates to 24
 |5(4)|---->    5*(24)<____________| evaluates to 120
 ------         finally back to main...

当函数以相反的方式返回时,它永远不知道它背后究竟是什么?堆栈中存储了激活记录,但是他们如何了解彼此弹出的对象以及谁在顶部? 堆栈如何跟踪函数内的所有变量 执行?除此之外,它如何跟踪执行的代码 (stackpointer)?从函数返回时调用结果 函数将填充占位符。通过使用stackpointer 但它如何知道继续执行代码的位置?这些是 堆栈如何工作的基础我知道但是对于递归我不知道 了解它究竟是如何运作的?

1 个答案:

答案 0 :(得分:1)

当一个函数返回时,它的堆栈帧被丢弃(即完整的本地状态从堆栈中弹出)。

详细信息取决于处理器架构和语言。

检查x86处理器的C调用约定:http://en.wikipedia.org/wiki/X86_calling_conventionshttp://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames并搜索&#34; PC汇编语言&#34;作者:Paul A. Carter(它有点过时,但它对ia32架构的C和Pascal调用约定有很好的解释)。

在x86处理器的C中:

一个。调用函数以相反的顺序将被调用函数的参数推送到堆栈,然后将指针推送到返回地址。

push -6
push 2
call add     # pushes `end:` address an then jumps to `add:` (add(2, -6))
end:
# ...

湾然后被调用的函数推送堆栈的基础(ia32中的ebp寄存器)(它用于引用调用函数中的局部变量)。

add:
push ebp

℃。被调用函数将ebp设置为当前堆栈指针(此ebp将是访问当前函数实例的局部变量和参数的引用)。

add:
# ...
mov ebp, esp

d。被调用的函数在堆栈中为本地(自动)变量保留空间,将变量的大小减去堆栈指针。

add:
# ...
sub esp, 4     # Reserves 4 bytes for a variable

即在被调用函数结束时,它将堆栈指针设置为ebp(即释放其局部变量),恢复调用函数的ebp寄存器并返回到返回地址(之前由调用者推送)。

add:
# ...
mov esp, ebp  # frees local variables
pop ebp       # restores old ebp
ret           # pops `end:` and jumps there

F。最后,调用者向堆栈指针添加被调用函数的参数所使用的空间(即释放参数使用的空间)。

# ...
end:
add esp, 8

返回值(除非它们大于寄存器)在eax寄存器中返回。