下面是一個階乘函數的c代碼。 我清楚地知道遞歸函數是如何工作的。 但我無法想象或不知道如何在MIPS堆棧上工作。MIPS堆棧可視化
MIPS CODE
我的問題是, 我們如何保存遞歸函數的棧(保存返回地址&參數)的遞歸的每一步?
我知道,對於每個遞歸函數,我們保存參數和返回地址,然後重新調整堆棧指針。
有人可以幫助我想象過程如何工作。
下面是一個階乘函數的c代碼。 我清楚地知道遞歸函數是如何工作的。 但我無法想象或不知道如何在MIPS堆棧上工作。MIPS堆棧可視化
MIPS CODE
我的問題是, 我們如何保存遞歸函數的棧(保存返回地址&參數)的遞歸的每一步?
我知道,對於每個遞歸函數,我們保存參數和返回地址,然後重新調整堆棧指針。
有人可以幫助我想象過程如何工作。
前提 MIPS中的堆棧與任何其他架構中的堆棧一樣,因此您可以僅使用Google。
假設堆點的內存的任意位置:
| xxx | xxx = Undefined
| xxx | <- $sp
| |
| | | Free area, stack moves down
| | V
現在只是模擬,比方說,fact(3)
通話。
您粘貼了代碼的圖像,因此此處不顯示任何代碼。一個可複製的代碼會使這個答案更加清晰。
fact
的每次調用按此順序推送返回地址和參數。
我們假設fact
位於0x1000,而fact
的調用位於0x1ffc。
fact(3)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | <- $sp Argument of fact(3)
| |
fact(2) called from fact(3)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
| 2 | <- $sp Argument of fact(2)
fact(1) called from fact(2)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
| 2 | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
| 1 | <- $sp Argument of fact(1)
fact(0) called from fact(1)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
| 2 | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
| 1 | Argument of fact(1)
| 0x1028 | Return address to L1+8 label
| 0 | <- $sp Argument of fact(0)
fact(0)
return 1並從堆棧中移除兩個項目。
fact(0)
是一個葉調用,即最後一次調用,所以沒有其他調用改變$ra
,此外$a0
(參數)不需要,因此保存在堆棧上的這兩個值通過增加$sp
來簡單地丟棄。
Just before "jr $ra" in fact(0)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
| 2 | Argument of fact(2)
| 0x1028 | Return address to L1+8 label
| 1 | <- $sp Argument of fact(1)
其他收益從堆棧恢復的$a0
和$ra
值來計算n*fact(n-1)
並返回到調用者。
Just before "jr $ra" in fact(1)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | Argument of fact(3)
| 0x1028 | Return address to L1+8 label
| 2 | <- $sp Argument of fact(2)
Just before "jr $ra" in fact(2)
| xxx |
| xxx |
| 0x2000 | Return address to caller of fact(3)
| 3 | <- $sp Argument of fact(3)
Just before "jr $ra" in fact(3)
| xxx |
| xxx | <- $sp
請注意堆棧如何返回到遞歸鏈末尾的原始位置。
從'事實'的角度來看,進入'事實'時的'xxx'值是「未定義的」,但是,可能更多正確地聲明爲「由呼叫者擁有」,因爲他們可能有值呼叫者放在那裏,不能改變。對於level 1調用'fact',它們是0級的堆棧幀。 –
感謝@Craig的註釋,我打算將「未定義」定義爲「在此上下文中未定義」,因爲調用者(* fact *)堆棧不是討論的一部分。我相信,只要他們能夠正確理解堆棧的工作原理,OP就會意識到你自己陳述的事實:) –
你幾乎可以解釋它。您每次打電話時都將這兩個值存儲在堆棧中。它是一個堆棧,因此它是一個線性的內存塊。我不知道如何做得更清楚。 –
只是每個遞歸調用的一些圖片將是偉大的,所以我對這個想法100%信心 –