web-dev-qa-db-ja.com

C ++グローバル変数の初期化順序

次のコード例が何をし、どのようにそれを行うのか理解できません。

_#include <stdio.h>

int f();

int a = f(); // a exists just to call f

int x = 22;

int f() {
    ++x;
    return 123; // unimportant arbitrary number
}

int main() {
    printf("%d\n", x);
}
_

これを実行すると、_23_が出力されます。これは直感的な答えです。

ただし、C++では、グローバル変数は 想定 定義順に初期化されます。 aの前に定義されているため、xの前にxを初期化する必要があることを意味します。その場合、fへの呼び出しはxの一部であるため、fが初期化される前に関数aを呼び出す必要があります。定義。

fが初期化される前にxが実際に呼び出された場合、それはfxをインクリメントしようとすることを意味します。確かに(ほとんどの場合UB、または何らかの意味不明な値)。次に、aが初期化された後、xは_22_に初期化され、プログラムは_22_を出力します。

明らかにそれは起こりません。しかし、何をしますか?そのコードは実際に何をしますか?

a = f()が評価される前にxが_22_に設定されているように見えますが、それは初期化の順序が逆になっていることを意味します(初期化とは間違っている可能性もあります) 、または発生した場合)。

35
corazza

問題は少し微妙です。詳細については、C++ 11 3.6.2を参照してください。

私たちにとって重要なのは、「静的ストレージ期間を持つ非ローカル変数」(または口語的な用語では「グローバル変数」)の初期化の2つのフェーズがあることです:静的初期化フェーズおよび動的初期化フェーズ。静的フェーズが最初になります。次のようになります。

int a = 0;
int x = 22;

その後、動的初期化が実行されます。

a = f();

ポイントは、静的初期化はまったく「実行」されないということです-コンパイル時に既知の設定値のみで構成されるため、実行前にこれらの値はすでに設定されています。初期化の理由int x = 22; staticは、初期化子が定数式であることです。


動的な初期化mayが静的な段階に引き上げられる場合があります(ただし、そうである必要はありません)が、これはこれらの場合の1つではありません。

初期化の動的バージョンは、初期化の前に名前空間スコープの他のオブジェクトの値を変更しません

この巻き上げが発生した場合、結果の初期値は、発生しなかった場合と異なる場合があります。このような「不定」初期化の1つの例が標準にあります。

34
Kerrek SB

また、考慮してください:

_#include <iostream>
using namespace std;

int f();
int g();

int a = f();
int b = g();

int main() {
        cout << a << " " << b;
}

int f() {
        b++;
        cout << "f" << endl;
        return 1;
}

int g() {
        cout << "g" << endl;
        return 2;
}
_

出力は次のとおりです。

_f
g
1 2
_

b = g();を_b = 22;_に置き換えると、_1 23_が出力されます。 Kerrek SBの答えは、これがなぜなのかを説明しています。

4
OSborn