時には、コールシーケンスを試して説明するのに役立ちます。
関数f1から関数f7のシーケンスを想像してください。ここで、f1はf2を呼び出し、f3はf3を呼び出すなどしてf7を呼び出します。:だからF1によって開始呼び出しシーケンスは、(0)、として示されるかもしれない(と他のは非常に似ています)
void f1(int n)
{
if(n==6)
return;
f2(++n);
std::cout<<n<<std::endl;
}
:(Iは0で開始するので、ない1)
F1は次のようになりますただNUMを交換、再帰を使用して移動するために、今すぐ
// no cout of 7
return to f6(6)
cout<<... n is 6
return to f5(5)
cout<<... n is 5
return to f4(4)
cout<<... n is 4
return to f3(3)
cout<<... n is 3
return to f2(2)
cout<<... n is 2
return to f1(1)
cout<<... n is 1
:
f1(0)--v : because f2 is called with ++n
f2(1)--v : because f3 is called with ++n etc.
f3(2)--v
f4(3)--v
f5(4)--v
f6(5)--v
f7(6)
そして、すべての関数呼び出し
はリターンを持っていますFOO
void foo(int n)
{
if(n==6)
return;
foo(++n);
std::cout<<n<<std::endl;
}
と呼び出しシーケンスを持つbered Fさん(fooで開始した(0))私は最初の部分の再帰を呼び出す
foo(0)--v -- given 0, calls foo with 1
foo(1)--v -- give 1, calls foo with 2
foo(2)--v -- etc
foo(3)--v
foo(4)--v
foo(5)--v
foo(6) - return
のように見えるかもしれません^^^^^^^^^^^ ^^^^^^^^^^^^^
return to foo(6) (was 5)
cout<<... n is 6
return to foo(5) (was 4)
cout<<... n is 5
return to foo(4) (was 3)
cout<<... n is 4
return to foo(3) (was 2)
cout<<... n is 3
return to foo(2) (was 1)
cout<<... n is 2
return to foo(1) (was 0)
cout<<... n is 1
第2の部分は、すべてのこれらの機能のリターンは、この特定のスタック用の「崩壊」は、私は時々「decursion」として注意(A古い軍事用語の新しい使用)。
coutが再帰呼び出しの後にあるため(つまり、デカッションで)、再帰呼び出し中に入力 'n'が増加しても出力シーケンスは減少しています。
行あたりしたがって、この特定の配列COUTの1桁:
6
5
4
3
2
1
アップデート - なぜ最後のコードスニップのクラッシュをしましたか?
再帰呼び出し後にポストインクリメントが発生するため、最後のコードスニップがクラッシュします。 foo(0)を呼び出すとfoo(n ++)が呼び出されますが、これは単にfoo(0)を再度呼び出すだけで、n(再び)をインクリメントします。 2回目以降のfoo()再帰はそれぞれ同じ値0を参照するため、終了条件(n == 6)は決して発生せず、スタックは無限関数呼び出しでオーバーフローします。
学習する最善の方法は、デバッガを使用してコードをステップ実行することです。そうすれば、その実行方法を正確に見ることができます。 – NathanOliver
「内部で何が起こっているのか教えてください。 - あなたのコードですよね?とにかく、オープニング・センテンスがそうです。だから、おそらくあなたは*それぞれの場合の*あなたの意図によって各実装で起こっていることをあなたが説明し、あなたがどこに間違っていたかを伝える必要があります。 – WhozCraig
print()の引数が++ n、(n + 1)、n ++のときに渡される値について考えてみましょう。 – FredK