2016-09-27 14 views
0

以下は階乗関数のCコードです。 私は、再帰関数がどのように機能するかをはっきりと理解しています。 私はMIPSスタック上で動作する方法を視覚化したり、わからないことがあります。MIPSスタック可視化

enter image description here

MIPSのCODE

enter image description here

私の質問は、 はどのように我々は、再帰の各ステップのスタックで(リターンアドレスに&の引数を保存)再帰関数を保存しているのですか?

各再帰関数に対して、引数と戻りアドレスを保存してから、スタックポインタを再調整することがわかりました。

誰かがプロセスの仕組みを視覚化するのを助けることができますか?

+0

あなたはそれについてかなり説明しました。これらの2つの値は、呼び出しが行われるたびにスタックに格納されます。これはスタックなので、メモリの線形ブロックです。どのようにしてそれをもっと明確にすることができないのか分かりません。 –

+0

それぞれの再帰呼び出しの写真はすばらしく、アイデアについて100%自信を持っています –

答えて

1

プレミス 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)戻り値1を返し、スタックから2つのアイテムを削除します。
fact(0)は最後の呼び出しであるため、他の呼び出しは変更されていません$raさらには$a0(引数)は必要ありません。したがって、スタックに保存されたこれらの2つの値は単に$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) 

他のリターンはn*fact(n-1)を計算し、その呼び出し元に戻るには、スタックから$a0$raの値を復元します。スタックは再帰鎖の末端に元の位置に戻される方法

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 

注意。

+0

'fact'への入力時の' xxx'値は 'fact'の観点からは「未定義」ですが、呼び出し元が値を持つ可能性があり、変更することはできないため、「呼び出し側が所有しています」と正しく指定されています。 'fact'へのレベル1コールでは、それらはレベル0のスタックフレームです。 –

+0

メモ@Craigのおかげで、呼び出し元(* fact *)のスタックは議論の一部ではないので、私は "このコンテキストでは定義されていない"として "未定義"を意図しました。私は、営業担当者がスタックの仕組みを正しく理解するとすぐに、あなた自身が述べた事実を理解すると確信しています:) –