BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #74381同步于 2013/10/11
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

哪位大神能给仔细分析一下这个递归过程中栈的变化,详细的啊。

z843259180
2013/10/11镜像同步13 回复
#include <stdio.h> void main() { int fun(int); int a; a=fun(4); printf("%d",a); } int fun(int b) { if(b==0) return 0; else if(b==1) return 1; else return (fun(b-1)+fun(b-2)); }
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
z843259180机器人#1 · 2013/10/11
【 在 z843259180 的大作中提到: 】 : #include <stdio.h> : void main() : { : ................... 补充一下哈,递归的思想我理解,所以希望大神们不要空洞的说,最好能给出栈状态的变化过程。。
jokerlee机器人#2 · 2013/10/11
fun函数的最前面加上 printf("enter %d\n", b); 最后加上 printf("exit %d\n", b); 不久能看到栈变化了么 更直观一点,传递一个depth变量用来缩进
z843259180机器人#3 · 2013/10/12
【 在 jokerlee 的大作中提到: 】 : fun函数的最前面加上 : printf("enter %d\n", b); : 最后加上 : ................... 谢谢你哈,我试试
nuanyangyang机器人#4 · 2013/10/12
初学AMD64汇编,试着解释一下……如何操作栈,和CPU有关,和操作系统有关,和calling convention有关,和编译器如何优化有关。x86(32位)和x86_64上不一样,都是x86_64,Windows和Linux不一样。 如果是x86_64和Linux系统的话,因为c语言里的int仍然是32位的,所以第一个参数b会用edi寄存器传入,返回值是eax。rdi,rsi,rdx,rcx,r8,r9归被调用的函数随便用(caller save,也就是调用者有责任保存),其余的寄存器如果想用的话,push一下也可以用。AMD64 ABI并不强制每次调用开始都要保存rbp并创建一个帧。 所以递归调用之前,只要能够把上述6个caller-save的寄存器中用过的(应该只有那个参数b的值(rdi),不会用别的寄存器了吧)压到栈上,用call会帮你压上RIP,然后返回的时候恢复一下就可以了。所以每一级递归,栈上可以只有上一层的b的值、前一次递归调用的返回值,以及返回地址。 当然,上述只是一种编译方案。
z843259180机器人#5 · 2013/10/12
【 在 nuanyangyang 的大作中提到: 】 : 初学AMD64汇编,试着解释一下……如何操作栈,和CPU有关,和操作系统有关,和calling convention有关,和编译器如何优化有关。x86(32位)和x86_64上不一样,都是x86_64,Windows和Linux不一样。 : 如果是x86_64和Linux系统的话,因为c语言里的int仍然是32位的,所以第一个参数b会用edi寄存器传入,返回值是eax。rdi,rsi,rdx,rcx,r8,r9归被调用的函数随便用(caller save,也就是调用者有责任保存),其余的寄存器如果想用的话,push一下也可以用。AMD64 ABI并不强制每次调用开始都要保存rbp并创建一个帧。 : 所以递归调用之前,只要能够把上述6个caller-save的寄存器中用过的(应该只有那个参数b的值(rdi),不会用别的寄存器了吧)压到栈上,用call会帮你压上RIP,然后返回的时候恢复一下就可以了。所以每一级递归,栈上可以只有上一层的b的值、前一次递归调用的返回值,以及返回地址。 : ................... 您说的这些我也看了一下,在一次函数调用的情况下我还能勉强搞清楚,可遇到这种稍微复杂一点的递归调用就混乱了。。每次返回的时候栈中的东西(包括一次调用的形参啦、下一条指令的地址啦、还有一些局部变量的存储空间啦、还有一些您提到的可能寄存器中的一些东西(没学过汇编不大了解。。。好像也不用纠结这些寄存器是吗))都要弹出来(当然仅是相对于这一次调用的东西)吗,还是咋弹出的。。表示混乱
tonyjansan机器人#6 · 2013/10/14
以Win7 IntelX86为例: int fun(int b) { if(b==0) return 0; else if(b==1) return 1; else return (fun(b-1)+fun(b-2)); } ; in main ; a = fun(4); mov [esp], 4 call _fun mov [esp+20h+var_4], eax ; ---------------------------------------- ; in fun ; init push ebp ; 保留栈基址 mov ebp, esp push ebx sub esp, 0x14 ; ---------------------------------------- cmp [ebp + 0x8], 0 ; if (b == 0) jnz short loc_401348 ; 不等则跳 mov eax, 0 jmp short loc_401373 ; return 0; ; ---------------------------------------- loc_401348: ; from jnz short loc_401348 cmp [ebp + 0x8], 1 ; if (b == 1) jnz short loc_401355 ; 不等则跳 mov eax, 1 jmp short loc_401373 ; return 1; ; ---------------------------------------- loc_401355: ; from jnz short loc_401355 mov eax, [ebp + 0x8] dec eax ; b - 1 mov [esp], eax call _fun ; 计算fun(b - 1),结果放入eax mov ebx, eax ; 结果暂存进ebx mov eax, [ebp + 0x8] ; 恢复eax = b sub eax, 2 ; b - 2 mov [esp], eax call _fun ; 计算fun(b - 2),结果放入eax add eax, ebx ; 此时eax为fun(b - 2)的结果,直接加ebx即可 ; ---------------------------------------- loc_401373: add esp, 0x14 ; 恢复堆栈,结果保存在eax中 pop ebx pop ebp retn 【 在 z843259180 的大作中提到: 】 : #include <stdio.h> : void main() : { : ...................
zxsword机器人#7 · 2013/10/14
直接加打印吧。 这个递归不用去了解底层=。=加了打印,一目了然那个先执行哪个后执行,楼主想要的就是这个吧。。。
nuanyangyang机器人#8 · 2013/10/14
嗯。楼上言之有理。对于C的用户,只需要知道:每次调用,都会创建它的执行环境,包括局部变量(也包括参数)和当前指令;而且上一层调用的执行环境是保存起来的。毕竟高级语言是抽象的。打印一下看看就行了。
z843259180机器人#9 · 2013/10/14
【 在 zxsword 的大作中提到: 】 : 直接加打印吧。 : 这个递归不用去了解底层=。=加了打印,一目了然那个先执行哪个后执行,楼主想要的就是这个吧。。。 加什么打印??在哪加??直接在fun()函数体最前面加printf("%d",b)吗??