例如:功能实施:
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 但它如何知道继续执行代码的位置?这些是 堆栈如何工作的基础我知道但是对于递归我不知道 了解它究竟是如何运作的?
答案 0 :(得分:1)
当一个函数返回时,它的堆栈帧被丢弃(即完整的本地状态从堆栈中弹出)。
详细信息取决于处理器架构和语言。
检查x86处理器的C调用约定:http://en.wikipedia.org/wiki/X86_calling_conventions,http://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
寄存器中返回。