Cの関数内の静的変数


119

何が印刷されますか?6 6または6 7?なぜ?

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
試してみる問題は何ですか?
Andrew

12
これを入力して、実際に見てみましたか?
ヴィルヘルムテル、2011

20
理由を知りたい。
Vadiklk、2011

7
@Vadiklkなので、「Why」で始まる質問をしてください
Andrey

1
ideone.com/t9Bbe何を期待しますか?結果はあなたの期待と一致しませんか?なぜ結果を期待したのですか?
eckes

回答:


186

ここには、寿命とスコープの2つの問題があります。

変数のスコープは、変数名を確認できる場所です。ここで、xは関数foo()内でのみ表示されます。

変数の存続期間は、変数が存在する期間です。キーワードstaticなしでxが定義された場合、存続期間はfoo()へのエントリーからfoo()からの戻りまでとなります。そのため、すべての呼び出しで5に再初期化されます。

キーワードstaticは、変数の存続期間をプログラムの存続期間まで延長するように機能します。たとえば、初期化は一度だけ行われ、その後、foo()へのすべての呼び出しにわたって、変数はその値を保持します。


15
@devanl、そうです。
orion elenzil 2016

1
シンプルで論理的:)
Dimitar Vukman '21

関数内で変数を静的として宣言する必要があるのはどのシナリオですか?これは以前に使用したことがないので、知りたいと思っていますか?
2018

感謝を申し上げますが、これはすべてページの一番上の先端で回答されました。人々は自分のコードを実行するだけではないので、私は笑わせます。xD
水たまり

この答えは間違っています。再帰関数について考える瞬間、ここで説明する定義は動作を説明しません!
フィリップ・クーリング

52

出力:6 7

理由:静的変数は(自動変数とは異なり)1回だけ初期化され、静的変数の追加の定義は実行時にバイパスされます。また、手動で初期化されていない場合は、値0によって自動的に初期化されます。そう、

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}


10

これは、次のプログラムがあるのと同じです。

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

staticキーワードがそのプログラムで行うすべてのことは、コンパイラーに(本質的に)「他のユーザーにアクセスさせたくない、他のユーザーに存在を知らせない変数がある」ということです。

メソッド内で、staticキーワードは上記と同じようにコンパイラーに指示しますが、「これがこの関数の外部に存在することをだれにも知らせず、この関数内でのみアクセス可能にする必要があります」。

これが役に立てば幸い


13
まあ、それは実際には同じではありません。Xにはまだスコープの問題があります。この例では、mainをつついてファッツすることができxます。グローバルです。元の例でxはfooにローカルで、そのブロック内でのみ表示されます。これは一般に推奨さxれます。fooが予測可能かつ表示可能な方法で維持される場合、他の人にそれを突き刺すことは一般に危険です。スコープ内foo() に維持するもう1つの利点として、foo()移植性も維持されます。
user2149140 2014年

2
@ user2149140「これはこの関数の外に存在することを誰にも言わないでください。この関数内でのみアクセス可能にする必要があります」
DCShannon

3
変数が宣言されている場所によるスコープの問題に対処しましたが、ライフタイムではなくスコープに影響を与える静的の説明は正しくないようです。
DCShannon 2015年

1
@Chameleon質問にはのタグが付けられているcため、このコンテキストでは、あなたの例はグローバルスコープでは違法になります。(Cはグローバルの定数初期化子を必要としますが、C ++は必要としません)。
Richard

5

関数内の静的変数は、プログラムが実行されている限り有効です。関数が呼び出されるたびに割り当てられることはなく、関数が戻るときに割り当てが解除されます。


これを「グローバル」変数のように言ってから、アクセスできないことを除いては、オキシモロンです。グローバルとは、どこからでもアクセスできることを意味します。静的な関数の場合、関数のどこからでもアクセスできません。他の人が指摘したように、OPの問題はスコープと寿命に関するものです。「グローバル」という用語を使用して、変数のスコープについて誤解させることを混同しないでください。
ChuckB 2017

@ChuckB:正解です。修正しました。さてそれは6年になります。私の以前の答えは6年前の認識を持っていました!
Donotalo

5

出力:6,7

理由

の宣言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

fooを最初に呼び出す前のブレークポイント


2

出力はになります6 7。静的変数(関数内かどうかに関係なく)は、その翻訳単位内の関数が実行される前に1回だけ初期化されます。その後は、変更されるまでその値を保持します。


1
staticは、関数が最初に呼び出される前ではなく、関数が呼び出される前に初期化されますか?
Jesse Pepper

@JessePepper:少なくともメモリが機能している場合、これはC ++ 98/03とC ++ 11のど​​ちらを使用しているかによって異なります。C ++ 98/03では、上記のとおりだと思います。C ++ 11では、スレッド化によってそれが本質的に不可能になっているため、関数の最初のエントリで初期化が行われます。
Jerry Coffin、

2
私はあなたが実際に間違っていると思います。C ++ 11より前でも、関数が呼び出されたときにのみ初期化されたと思います。これは、静的初期化依存関係の問題に対する一般的な解決策にとって重要です。
Jesse Pepper、

2

ヴァディクル

なぜ ...?理由は、静的変数は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

静的変数に関するウィキペディアの記事を読んでみましょう...

静的ローカル変数:関数内で静的として宣言された変数は、自動ローカル変数と同じスコープを持ちながら静的に割り当てられます。したがって、1回の呼び出しで関数が静的ローカル変数に入力した値は、関数が再度呼び出されたときにも存在します。


5
それはひどい!「関数内で静的として宣言された変数は静的に割り当てられます」-意味がわかっていない限り、説明はありません。

@ブランク:まあ、それは私が2番目の文がそうであると思ったものです。私はあなたが正しいと思うが、それはより良い言葉にする必要があります。
Andrew White

また、これは本当に混乱する部分には対応していません。これは、初期化子が後続の呼び出しでスキップされるという事実です。
トムオージェ

静的に割り当てられるということは、スタックもヒープもないことを意味します。
カメレオン

1

簡単にテストできるように、6 7が印刷されます。これfooが理由です。が最初に呼び出されると、静的変数xは5に初期化されます。次に、6にインクリメントされて印刷されます。

次のへの呼び出しのためにfoo。プログラムは静的変数の初期化をスキップし、代わりに前回xに割り当てられた値6を使用します。実行は通常どおり進行し、値は7になります。


1
6 7

xは、foo()からのみ参照できるグローバル変数です。5は、コードの.dataセクションに格納されている初期値です。後続の変更は以前の値を上書きします。関数本体で生成される割り当てコードはありません。


1

6と7静的変数は1回だけ初期化されるため、5 ++は最初の呼び出しで6になります。6++は2番目の呼び出しで7になります。


0

少なくとも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'の場合、これは必須ではなく、コンパイラーが出力アセンブリコードに格納した値を使用して、プログラムのロード時に変数を初期化できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.