ここからは、プログラムの技法の1つである「再帰呼び出し」について説明します。
便利な考え方ですが、良く分からずに使うとプログラムが止まってしまいます。
まずは、仕組みから説明しましょう。
「再帰呼び出し」とは、実行中の関数の中で、その関数を再度呼び出すことを指します。
言葉だけでは分かりづらいので、一番単純な例を書きます。
※まだ実行はしないで下さい。
<sample program 147-01>
int main(void) { main(); return 0; } |
意味の無いプログラムですが、main関数の中でmain関数を呼び出しています。
とりあえず実行してみましょうか。
<実行結果>
私の環境ではこのような結果になってしまいまいした・・・
main関数の実行中にmain関数を呼び出す訳ですから、いつまで経ってもプログラムが終わりません。
いわゆる形を変えた無限ループのようなものです。
しかし、無限ループであれば途中で止まったりせず、無限に実行し続けるはずです・・・
では、どのような仕組みになっているか説明します。
関数が呼び出される時には、戻る場所(実際はプログラムのアドレス)というものが保存されます。
大体、呼び出された次の行が設定されます。
<sample program 147-01>
int main(void)
{
main();
return 0; /* ←ここが戻り場所 */
}
|
最初のmain関数の実行で、戻り場所が設定され、自分自身(main関数)を呼び出します。
すると、またmain関数を呼び出そうとするので、新しい戻り場所を追加します。
これを繰り返す内に、戻り場所を設定するメモリ領域(スタックといいます)が不足し、プログラムがおかしくなってしまいます。
当然気づいている人もいると思いますが、コンパイルした時点で警告が出ています。
warning C4717: 'main': すべてのコントロールのパス、関数を回帰するとランタイム スタック オーバーフローが発生します。
「スタックオーバーフロー」という言葉が出ています。
「スタック」があふれてしまい、プログラムが実行出来なくなるということです。
※スタックについてはデータ構造の章で説明します。
では、だいたい何回くらいで止まっているのでしょうか?
次のプログラムで試してみましょう。
<sample program 147-02>
#include <stdio.h> int main(void) { static int counter; counter++; printf("%d\n", counter); main(); return 0; } |
static変数を使えば、関数の呼び出し回数を数えることが出来ます。
では、実行してみましょう。
<実行結果>
1 2 3 4 ・ ・ ・ 4847 4848 4849
私の環境では、4849回で止まってしまいました。
この数字は、この実行環境では必ず4849回再帰呼び出しが出来ることを保証するものではありません。
関数内のローカル変数の有無でも変わってしまいます。
再帰呼び出しには限度があることを理解しておいてください。