セベスタによるプログラミング言語の概念の本で述べたように:
- 静的変数はグローバル アクセスを提供し、サブプログラムの呼び出し間で値を保持でき (履歴に依存)、効率的です。
- 静的変数は再帰をサポートしていません
静的変数が再帰をサポートしないのはなぜですか? これは、再帰が発生すると大量のメモリが浪費され、プログラム全体がstatic
終了するまでメモリから割り当てが解除されないためですか?
静的フィールドを再帰アルゴリズムで使用するのが難しいのは、それらが静的であるということではなく、アクティベーションに関連付けられていないということです。非静的フィールドは、再帰アルゴリズムで効果的に使用することも同様に困難です。
さらに、問題は、再帰アルゴリズムでフィールドを使用するのが難しいことではなく、より一般的には、再入可能アルゴリズム、または同じコードが複数のスレッドで呼び出されるアルゴリズムでフィールドを使用するのが難しいことです。
ローカル変数と仮パラメーターが再帰やその他の再入可能なシナリオで役立つのは、それらがアクティベーションに関連付けられていることです。
要するに、この本は具体的すぎるため、混乱を招きます。白いテーブルの上に茶色の卵を置くのは難しいと言っているようなものです。それは本当ですが、茶色と白はそれと何の関係があるのでしょうか? どのテーブルでも卵のバランスをとるのは難しいです。再入可能なシナリオで任意のフィールドを正しく使用することは難しいため、再帰呼び出しで静的フィールドを正しく使用することは困難です。
各再帰呼び出しは同じ静的変数にアクセスし、他の呼び出しによって変数に格納された値を上書きします。静的変数が、関数が呼び出された回数をカウントするなど、純粋に反復的な目的を果たしている場合を除き、再帰を行う場合、これは一般的に望ましくありません。
おそらく、再帰を使用する場合にメモリがどのように管理されるかは、コード例で説明できます。そして、再帰ルーチンで静的変数を使用することはスターターではありません(メソッドがどのように呼び出されたかに関係なく、関数が呼び出された回数をカウントするためのグローバルカウンターが必要な場合は例外です(たとえば、マルチスレッドまたは連続から)電話))
この C コードを考えると:
#include <iostream>
using namespace std;
void callMe(int j) {
static int i = 0;
++i;
++j;
printf("Loop: %d\n", i);
printf("i memory location: %p\n", &i);
printf("j memory location: %p\n", &j);
printf("\n");
// change i to j ,
// so the other next method invocations
// or simultaneous(e.g. multi-threaded) method invocations
// of this method can work independently from
// other method invocations / simultaneous method invocations
if ( i < 10 )
callMe(j);
printf("Returning from loop %d\n", j);
}
int main() {
callMe(0);
printf("Next\n");
callMe(0);
return 0;
}
出力:
Loop: 1
i memory location: 0x804a038
j memory location: 0xbf8b69c0
Loop: 2
i memory location: 0x804a038
j memory location: 0xbf8b69b0
Loop: 3
i memory location: 0x804a038
j memory location: 0xbf8b69a0
Loop: 4
i memory location: 0x804a038
j memory location: 0xbf8b6990
Loop: 5
i memory location: 0x804a038
j memory location: 0xbf8b6980
Loop: 6
i memory location: 0x804a038
j memory location: 0xbf8b6970
Loop: 7
i memory location: 0x804a038
j memory location: 0xbf8b6960
Loop: 8
i memory location: 0x804a038
j memory location: 0xbf8b6950
Loop: 9
i memory location: 0x804a038
j memory location: 0xbf8b6940
Loop: 10
i memory location: 0x804a038
j memory location: 0xbf8b6930
Returning from loop 10
Returning from loop 9
Returning from loop 8
Returning from loop 7
Returning from loop 6
Returning from loop 5
Returning from loop 4
Returning from loop 3
Returning from loop 2
Returning from loop 1
Next
Loop: 11
i memory location: 0x804a038
j memory location: 0xbf8b69c0
Returning from loop 1
ご覧のとおり、static i
変数はメモリ内に 1 つの場所しかありません。そのため、値は呼び出し間で存続できます。特定の問題を分割して征服したい場合、特にマルチスレッドのメソッド呼び出しで、これは望ましくありません。メソッドで2 つの同時呼び出しが行われた場合callMe
、これらのメソッド呼び出しは同じ変数インスタンス (静的変数) を使用しているだけであるため、他の callMe はそのタスクを完了できない可能性があります。これらのメソッド呼び出しは互いに副作用を持ち、これらのメソッド変数の独立したコピーがないため、互いに独立して動作することはできません。
上記のコードはマルチスレッド化されていなくても、次のメソッド呼び出しはそのタスクを完了できません。2 番目の呼び出しが同じ変数 (静的変数) にアクセスし、以前のメソッド呼び出しによって既に汚染された値を受け取るためです
簡単に言えば、静的変数は関数内にある場合でも、依然としてグローバル変数です。関数内の静的変数は名前の衝突を防ぐだけですが、すべての意図と目的において、静的変数はグローバル変数です
コードを意図したとおりに実行するには、をに変更if (i < 10)
しますif (j < 10)
ちなみに、非静的変数には独自のメモリが割り当てられ、スタックに割り当てられます。停止条件がない場合、多くの再帰呼び出しの後、上記のコードはスタック オーバーフロー エラーを生成します。これが stackoverflow の名前の由来です。言うまでもなく、プログラマーは再帰が好きですツ</p>
ライブテスト: http://ideone.com/Xl86q
再帰は、オブジェクトの複数のインスタンスを階層化し、各インスタンスが独自のデータを保持します。静的変数はすべてのインスタンスで共有されます。静的変数は再帰プロセスで使用でき、定数として使用する場合は適切ですが、動的に使用するとデバッグが困難になる可能性があると確信しています。