web-dev-qa-db-ja.com

Cの関数内の静的変数

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

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

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

ここには、ライフタイムとスコープの2つの問題があります。

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

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

キーワードstaticは、変数の有効期間をプログラムの有効期間まで延長するように機能します。例えば初期化は一度だけ行われ、その後変数はその値を保持します-どんなものであれ-foo()のすべての将来の呼び出しにわたって。

154
user82238

出力: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;
}
44
Nitesh Borad

6 7

コンパイラーは、関数に入るたびに静的変数の初期化が行われないように調整します

9
Chaim Geretz

これは、次のプログラムを使用するのと同じです。

static int x = 5;

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

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

そのプログラムで静的キーワードが行うことは、コンパイラに(本質的に)「ねえ、私はここに他の誰にもアクセスさせたくない変数があります。

メソッド内では、静的キーワードはコンパイラに上記と同じことを伝えますが、「この関数の外部に存在することを誰にも伝えないでください。この関数の内部でのみアクセス可能にする必要があります」。

これが役立つことを願っています

9

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

5
Donotalo

出力: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つでこれを読みました:「また、これは、初期化子が後続の呼び出しでスキップされるという事実である、本当に紛らわしい部分に対処しません。」すべての呼び出しでスキップされました。変数の初期化は、適切な機能コードの範囲外です。

Fooがまったく呼び出されるかどうかに関係なく、理論的には5の値が設定されます。ただし、どこでも呼び出さない場合、コンパイラーは関数を最適化する場合があります。 fooが呼び出される前に、変数に5の値が必要です。

fooの内部では、ステートメントstatic int x = 5;がコードを生成することはほとんどありません。

関数xを私のプログラムに入れたときにfooが使用するアドレスを見つけ、そのプログラムを再度実行すると同じ場所が使用されると(正しく)推測しました。以下の部分的な画面キャプチャは、xの最初の呼び出しの前であっても、fooの値が5であることを示しています。

Break Point before first call to foo

3
Ivan

ヴァディクルク、

なぜ ...?理由は、静的変数は一度だけ初期化され、プログラム全体でその値を維持するためです。つまり、関数呼び出し間で静的変数を使用できます。また、「関数が呼び出される回数」をカウントするためにも使用できます

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

答えは5 4 3 2 1で、5 5 5 5 5 5 ....(無限ループ)ではありません。繰り返しますが、理由は静的変数が一度初期化され、次回main()が呼び出されたときに、プログラムで既に初期化されているため、5に初期化されません。したがって、値を変更できますが、再初期化はできません。それが静的変数の仕組みです。

または、ストレージごとに考慮することができます。静的変数はプログラムのデータセクションに格納され、データセクションに格納される変数は一度初期化されます。そして、初期化の前にBSSセクションに保持されます。

次に、Auto(local)変数がStackに格納され、そのために新しいFAR(function activation record)が作成されると、関数が呼び出されるたびに、スタック上のすべての変数が再初期化されます。

理解を深めるために、上記の例を「静的」なしで実行し、何が出力されるかを知らせます。これにより、これら2つの違いを理解できます。

おかげでJaved

2
Javed

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

2
Jerry Coffin

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

1
Tushar shirsath

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

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

1
Andrew White

簡単にテストできるように、6 7が出力されます。その理由は次のとおりです。fooが最初に呼び出されると、静的変数xが5に初期化されます。

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

6 7

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

1
mouviciel

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

0
user5122888