関数レベルの静的変数はいつ割り当て/初期化されますか?

StackOverflow https://stackoverflow.com/questions/55510

  •  09-06-2019
  •  | 
  •  

質問

私は、グローバルに宣言された変数がプログラムの開始時に割り当てられる (および、該当する場合は初期化される) と確信しています。

int globalgarbage;
unsigned int anumber = 42;

しかし、関数内で定義された静的なものはどうなるでしょうか?

void doSomething()
{
  static bool globalish = true;
  // ...
}

スペースはいつですか globalish 割り当てられていますか?番組がいつ始まるかだと思います。しかし、その場合も初期化されるのでしょうか?それとも初期化されるのでしょうか? doSomething() 最初に呼ばれますか?

役に立ちましたか?

解決

これに興味があったので、次のテストプログラムを作成し、g++ バージョン 4.1.2 でコンパイルしました。

include <iostream>
#include <string>

using namespace std;

class test
{
public:
        test(const char *name)
                : _name(name)
        {
                cout << _name << " created" << endl;
        }

        ~test()
        {
                cout << _name << " destroyed" << endl;
        }

        string _name;
};

test t("global variable");

void f()
{
        static test t("static variable");

        test t2("Local variable");

        cout << "Function executed" << endl;
}


int main()
{
        test t("local to main");

        cout << "Program start" << endl;

        f();

        cout << "Program end" << endl;
        return 0;
}

結果は私が期待していたものではありませんでした。静的オブジェクトのコンストラクターは、関数が初めて呼び出されるまで呼び出されませんでした。出力は次のとおりです。

global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed

他のヒント

C++ 標準からの関連する文言の一部:

3.6.2 非ローカルオブジェクトの初期化 [basic.start.init]

1

静的ストレージ期間のオブジェクトのストレージ(基本.stc.静的) はゼロで初期化されます (dcl.init)他の初期化が行われる前。ポッドタイプのオブジェクト(基本的なタイプ)一定の式で初期化された静的ストレージ期間(expr.const)動的初期化が行われる前に初期化するものとする。同じ翻訳ユニットで定義され、動的に初期化された静的ストレージ持続時間を備えた名前空間範囲のオブジェクトは、定義が翻訳ユニットに表示される順序で初期化されるものとします。[注記: dcl.init.aggr 集約メンバーが初期化される順序について説明します。ローカル静的オブジェクトの初期化について説明します stmt.dcl. ]

[コンパイラ作成者の自由をさらに追加する以下の詳細テキスト]

6.7 宣言文[stmt.dcl]

...

4

ゼロ初期化 (dcl.init)静的ストレージ期間のすべてのローカルオブジェクトの(基本.stc.静的)他の初期化が行われる前に実行されます。ポッドタイプのローカルオブジェクト(基本的なタイプ)一定の表現で初期化された静的ストレージ期間が最初に入力される前に初期化されます。実装は、名前空間範囲の静的ストレージ持続時間を持つオブジェクトを静的に初期化するのと同じ条件下で、静的ストレージ期間の他のローカルオブジェクトの早期初期化を実行することが許可されています(基本的な.start.init)。それ以外の場合、そのようなオブジェクトは、最初にコントロールが宣言を通過するときに初期化されます。このようなオブジェクトは、初期化の完了時に初期化されたと見なされます。例外をスローして初期化が終了する場合、初期化は完了していないため、次にコントロールが宣言に入ると再試行されます。コントロールがオブジェクトが初期化されている間に(再帰的に)宣言に再び入ると、動作は未定義です。[例:

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

--終わりの例]

5

静的ストレージ期間のローカルオブジェクトのデストラクタは、変数が構築された場合にのみ実行されます。[注記: 基本的な開始期間 静的ストレージ期間のあるローカルオブジェクトが破壊される順序について説明します。】

すべての静的変数のメモリはプログラムのロード時に割り当てられます。ただし、ローカル静的変数は、プログラムの起動時ではなく、初めて使用されるときに作成および初期化されます。それと静力学全般については、良い本がいくつかあります。 ここ. 。一般に、これらの問題の一部は実装に依存すると思います。特に、このようなものがメモリ内のどこに配置されるかを知りたい場合はそうです。

コンパイラは関数で定義された静的変数を割り当てます。 foo ただし、プログラムのロード時に、コンパイラは関数に追加の命令 (マシンコード) も追加します。 foo 初めて呼び出されるときに、この追加コードが静的変数を初期化するようにします (例:該当する場合はコンストラクターを呼び出します)。

@アダム:コンパイラによるこの舞台裏でのコードの挿入が、ご覧のような結果の原因です。

コードを再度テストしてみます アダム・ピアース さらに 2 つのケースを追加しました。クラスおよび POD タイプの静的変数。私のコンパイラは Windows OS(MinGW-32) の g++ 4.8.1 です。結果はクラス内の静的変数であり、グローバル変数と同じように扱われます。そのコンストラクターは、main 関数に入る前に呼び出されます。

  • 結論 (g++、Windows 環境の場合):

    1. グローバル変数 そして クラス内の静的メンバー:Enter の前にコンストラクターが呼び出されます 主要 関数 (1).
    2. ローカル静的変数:コンストラクターは、実行が最初に宣言に到達したときにのみ呼び出されます。
    3. もし ローカル静的変数はPODタイプです, 、その後、入力する前にも初期化されます 主要 関数 (1)。POD タイプの例: 静的整数 = 10;

(1):正しい状態は次のようになります。 「同じ翻訳単位からの関数が呼び出される前」。 ただし、以下の例のように単純な場合は、次のようになります。 主要 関数。

<iostream> を含める

#include < string>

using namespace std;

class test
{
public:
   test(const char *name)
            : _name(name)
    {
            cout << _name << " created" << endl;
    }

    ~test()
    {
            cout << _name << " destroyed" << endl;
    }

    string _name;
    static test t; // static member
 };
test test::t("static in class");

test t("global variable");

void f()
{
    static  test t("static variable");
    static int num = 10 ; // POD type, init before enter main function

    test t2("Local variable");
    cout << "Function executed" << endl;
}

int main()
{
    test t("local to main");
    cout << "Program start" << endl;
    f();
    cout << "Program end" << endl;
    return 0;
 }

結果:

static in class created
global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
static in class destroyed

Linux環境でテストした人はいますか?

静的変数はコード セグメント内に割り当てられます。静的変数は実行可能イメージの一部であるため、すでに初期化された状態でマップされます。

関数スコープ内の静的変数は同じように扱われ、スコープは純粋に言語レベルの構造です。

このため、静的変数は (何かを指定しない限り) 未定義の値ではなく 0 に初期化されることが保証されます。

初期化には他にも活用できる側面がいくつかあります。たとえば、共有セグメントを使用すると、同時に実行される実行可能ファイルの異なるインスタンスが同じ静的変数にアクセスできます。

C++ (グローバル スコープの) では、静的オブジェクトは、C ランタイム ライブラリの制御下で、プログラムの起動の一部として呼び出されるコンストラクターを持ちます。Visual C++ では、少なくともオブジェクトが初期化される順序は、 初期化セグメント プラグマ。

それとも doSomething() が最初に呼び出されたときに初期化されますか?

はい、そうです。これにより、特に、try/catch ブロック内など、適切な場合にグローバルにアクセスされるデータ構造を初期化できます。例えば。の代わりに

int foo = init(); // bad if init() throws something

int main() {
  try {
    ...
  }
  catch(...){
    ...
  }
}

あなたは書ける

int& foo() {
  static int myfoo = init();
  return myfoo;
}

それを try/catch ブロック内で使用します。最初の呼び出しで、変数は初期化されます。その後、最初と次の呼び出しで、その値が (参照によって) 返されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top