何が印刷されますか?6 6または6 7?なぜ?
void foo()
{
static int x = 5;
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
何が印刷されますか?6 6または6 7?なぜ?
void foo()
{
static int x = 5;
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
回答:
ここには、寿命とスコープの2つの問題があります。
変数のスコープは、変数名を確認できる場所です。ここで、xは関数foo()内でのみ表示されます。
変数の存続期間は、変数が存在する期間です。キーワードstaticなしでxが定義された場合、存続期間はfoo()へのエントリーからfoo()からの戻りまでとなります。そのため、すべての呼び出しで5に再初期化されます。
キーワードstaticは、変数の存続期間をプログラムの存続期間まで延長するように機能します。たとえば、初期化は一度だけ行われ、その後、foo()へのすべての呼び出しにわたって、変数はその値を保持します。
これは、次のプログラムがあるのと同じです。
static int x = 5;
void foo()
{
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
staticキーワードがそのプログラムで行うすべてのことは、コンパイラーに(本質的に)「他のユーザーにアクセスさせたくない、他のユーザーに存在を知らせない変数がある」ということです。
メソッド内で、staticキーワードは上記と同じようにコンパイラーに指示しますが、「これがこの関数の外部に存在することをだれにも知らせず、この関数内でのみアクセス可能にする必要があります」。
これが役に立てば幸い
x
ます。グローバルです。元の例でx
はfooにローカルで、そのブロック内でのみ表示されます。これは一般に推奨さx
れます。fooが予測可能かつ表示可能な方法で維持される場合、他の人にそれを突き刺すことは一般に危険です。スコープ内foo()
に維持するもう1つの利点として、foo()
移植性も維持されます。
c
ため、このコンテキストでは、あなたの例はグローバルスコープでは違法になります。(Cはグローバルの定数初期化子を必要としますが、C ++は必要としません)。
関数内の静的変数は、プログラムが実行されている限り有効です。関数が呼び出されるたびに割り当てられることはなく、関数が戻るときに割り当てが解除されます。
の宣言x
は内部にfoo
ありx=5
ますが、初期化は外部で行われますfoo
!のます。
ここで理解する必要があるのは
static int x = 5;
と同じではありません
static int x;
x = 5;
他の回答では、ここで重要な用語であるスコープとライフタイムを使用しており、スコープはx
関数内での宣言の時点から関数foo
の終わりまでであると指摘していますfoo
。たとえば、宣言を関数の最後に移動して確認したx
ところ、x++;
ステートメントで。
だからstatic int x
文の(範囲)の部分は、実際にあなたがそれを読んで、どこかの適用INSIDEを関数の中ではない、その上、唯一そこから以降の機能と。
しかし、x = 5
文の(寿命)部分は変数の初期化と起こっOUTSIDEプログラムロードの一部として機能します。変数x
は次の値で生まれます5
、プログラムが読み込まれたときのます。
私はこれをコメントの1つで読みました:「また、これは本当に混乱する部分に対処しません。これは、初期化子が後続の呼び出しでスキップされるという事実です。」すべての呼び出しでスキップされます。変数の初期化が適切な関数コードの外にあります。
5の値は、fooがまったく呼び出されたかどうかに関係なく、理論的には設定されますが、どこにも呼び出さない場合、コンパイラーは関数を最適化します。fooが呼び出される前に、値5が変数に含まれている必要があります。
内部でfoo
は、ステートメントstatic int x = 5;
がコードを生成することはほとんどありません。
私のプログラムにx
関数をfoo
挿入するときにアドレスが使用することがわかりました。次に、プログラムをもう一度実行すると同じ場所が使用されると(正しく)推測しました。以下の部分的なスクリーンキャプチャx
は、5
への最初の呼び出しの前でも値がであることを示していますfoo
。
出力はになります6 7
。静的変数(関数内かどうかに関係なく)は、その翻訳単位内の関数が実行される前に1回だけ初期化されます。その後は、変更されるまでその値を保持します。
ヴァディクル
なぜ ...?理由は、静的変数は1回だけ初期化され、プログラム全体でその値を維持するためです。つまり、関数呼び出しの間に静的変数を使用できます。また、「関数が呼び出された回数」をカウントするためにも使用できます。
main()
{
static int var = 5;
printf("%d ",var--);
if(var)
main();
}
答えは5 4 3 2 1であり、5 5 5 5 5 5 5ではありません...(無限ループ)期待どおりです。繰り返しますが、理由は静的変数が1回初期化され、次回main()が呼び出されたときに、プログラムですでに初期化されているため、5に初期化されません。したがって、値を変更できますが、再初期化できません。これが静的変数のしくみです。
または、ストレージごとに検討できます。静的変数はプログラムのデータセクションに格納され、データセクションに格納されている変数は一度初期化されます。また、初期化前はBSSセクションに保持されます。
次に、Auto(local)変数はStackに格納され、スタック上のすべての変数は、関数が呼び出されると常に再初期化され、そのために新しいFAR(関数アクティベーションレコード)が作成されます。
さらに理解するには、「静的」なしで上記の例を実行し、何が出力されるかを知らせてください。これにより、これら2つの違いを理解することができます。
ありがとうJaved
静的変数に関するウィキペディアの記事を読んでみましょう...
静的ローカル変数:関数内で静的として宣言された変数は、自動ローカル変数と同じスコープを持ちながら静的に割り当てられます。したがって、1回の呼び出しで関数が静的ローカル変数に入力した値は、関数が再度呼び出されたときにも存在します。
簡単にテストできるように、6 7が印刷されます。これfoo
が理由です。が最初に呼び出されると、静的変数xは5に初期化されます。次に、6にインクリメントされて印刷されます。
次のへの呼び出しのためにfoo
。プログラムは静的変数の初期化をスキップし、代わりに前回xに割り当てられた値6を使用します。実行は通常どおり進行し、値は7になります。
6と7静的変数は1回だけ初期化されるため、5 ++は最初の呼び出しで6になります。6++は2番目の呼び出しで7になります。
少なくともC ++ 11では、ローカル静的変数の初期化に使用される式が 'constexpr'ではない(コンパイラーで評価できない)場合、関数の最初の呼び出し中に初期化を行う必要があります。最も簡単な例は、パラメーターを直接使用してローカル静的変数を初期化することです。したがって、コンパイラは、呼び出しが最初の呼び出しであるかどうかを推測するコードを発行する必要があります。これには、ローカルのブール変数が必要です。私はそのような例をコンパイルして、アセンブリコードを見てこれが正しいことを確認しました。例は次のようになります。
void f( int p )
{
static const int first_p = p ;
cout << "first p == " << p << endl ;
}
void main()
{
f(1); f(2); f(3);
}
もちろん、式が 'constexpr'の場合、これは必須ではなく、コンパイラーが出力アセンブリコードに格納した値を使用して、プログラムのロード時に変数を初期化できます。