-3

私は本当にこのコードを理解できません。関数が自分自身を呼び出すと、実際に何が起こるでしょうか? スタックの概念に関連していることはわかっていますが、それでもこれらの質問を解決できません。

#include<stdio.h>

fun(int);

main()
{
  int x=3;
  fun(x);
}

fun(int a) 
{
  if(a<0)
   {
     fun(--a);    // what happens when function calls itself
     printf("%d",a);
     fun(--a);
   }
} 

この間に発生する一連のイベントを説明してください。

4

3 に答える 3

4

この場合、fun() の呼び出しは、他の関数の呼び出しと同じです。例えば:

int main() {
   int a = 0;
   foo(a);
   printf("main a = %d\n", a);
}

void foo(int a) {
   a = 1;
   bar(a);
   printf("foo a = %d\n", a);
}

void bar(int a) {
   a = 2;
   printf("bar a = %d\n", a);
}

呼び出しシーケンスは次のようになります。

main();
foo();
bar();

出力は次のようになります。

bar a = 2
foo a = 1
main a = 0

引数は値で渡されるため、aコピーされ、実際には各関数で異なる変数になります。再帰でも同じことが起こります。

main();  x = 3
fun(3);  a = 3, so a > 0, nothing happens, return to main()

条件を変更して、 a > 0 のときに fun() が自分自身を呼び出す場合 (トップダウンで読む)

main();  x = 3
fun(3);  a = 3, a > 0 so --a = 2, fun(2)
fun(2);  a = 2, a > 0 so --a = 1, fun(1)
fun(1);  a = 1, a > 0 so --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  printf("%d", a) displays 1, --a = 0, fun(0)  /* same as fun(1) above */
fun(0);  a = 0, so return to fun(1)

fun(1);  nothing left to do so return to fun(2)       /* same as fun(1) above */

fun(2);  printf("%d", a) displays 2, --a = 1, fun(1)
fun(1);  a = 1, a > 0 so --a = 0, fun(0)              /* this is a new fun(1) */
fun(0);  a = 0, so return to fun(1)

fun(1);  printf("%d", a) displays 1, --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  nothing left to do so return to fun(2)

fun(2);  nothing left to do so return to fun(3)

fun(3);  printf("%d", a) displays 3, --a = 2, fun(2)  /* halfway point */
fun(2);  a = 2, a > 0 so --a = 1, fun(1)
fun(1);  a = 1, a > 0 so --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  printf("%d", a) displays 1, --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  nothing left to do so return to fun(2)

fun(2);  printf("%d", a) displays 2, --a = 1, fun(1)
fun(1);  a = 1, a > 0 so --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  printf("%d", a) displays 1, --a = 0, fun(0)
fun(0);  a = 0, so return to fun(1)

fun(1);  nothing left to do so return to fun(2)

fun(2);  nothing left to do so return to fun(3)

fun(3);  nothing left to do so return to main()

出力は次のようになります: 1213121 これは呼び出しのツリー構造を反映しています:

        3
       / \
      /   \
     2     2
    / \   / \
   1   1 1   1
于 2012-08-01T13:24:49.400 に答える
1

関数の引数はCの値で渡されます。つまり、関数が呼び出されるたびに一時的なローカル変数が作成されます。関数が再帰的に呼び出されると、毎回新しい変数のセットが作成されます。ただし、処理中の値のスタックをどこかで維持する必要があるため、再帰によってストレージスペースが節約されるとは限りません。

于 2012-08-01T12:50:22.993 に答える
0

関数は、メモリ内のどこかに常駐する単なるコードです。関数呼び出しを行うたびに、コンパイラはそれをプラットフォーム用のアセンブリ コード コマンドに変換し、関数呼び出しの完了後に実行される次のコマンドのアドレスを保存し、プロセッサにメモリ内の文字通り「ジャンプ」する場所を伝えます。次に実行するコマンドを読み取るため。

再帰が機能するのは、メモリ内の関数のコード ブロックの先頭に「ジャンプ」するようにプロセッサに簡単に指示できるためです。呼び出し元の現在の関数は、他の関数と同様にメモリ アドレスを持っているため、プロセッサがメモリ内の現在の関数のコード ブロックの先頭にジャンプしたり、メモリ内の他の関数のコード ブロックにジャンプしたりすることに違いはありません。

関数呼び出しの完了後に実行するコマンドの戻りアドレスと、現在の関数の引数と自動変数を格納する場所を保存する必要があるため、スタックが機能します。そのため、関数呼び出しを連続して行うと、コール スタックが作成されます。これには、引数と、スタックが下方向に成長している場合はスタックの上位にある以前に呼び出された関数の戻りアドレスが含まれます。これを関数の「スタックフレーム」と総称します。関数から戻ると、現在の関数のスタック フレームがスタックの一番下からポップされ、関数の完了後にプロセッサがジャンプして戻る必要があるメモリ アドレスが読み取られ、実行されます。再帰の場合、

于 2012-08-01T12:59:58.177 に答える