93

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

int globalgarbage;
unsigned int anumber = 42;

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

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

スペースはいつglobalish割り当てられますか? 番組が始まると思います。しかし、それも初期化されますか?doSomething()または、最初に呼び出されたときに初期化されますか?

4

8 に答える 8

96

私はこれに興味があったので、次のテスト プログラムを作成し、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
于 2008-09-11T00:18:08.460 に答える
60

C++ 標準からのいくつかの関連する言い回し:

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

1

静的ストレージ期間 ( basic.stc.static ) を持つオブジェクトのストレージは、他の初期化が行われる前にゼロで初期化されます ( dcl.init )。定数式 ( expr.const ) で初期化された静的ストレージ期間を持つPOD 型 ( basic.types ) のオブジェクトは、動的初期化が行われる前に初期化されます。同じ翻訳単位で定義され、動的に初期化される静的ストレージ期間を持つ名前空間スコープのオブジェクトは、それらの定義が翻訳単位に現れる順序で初期化されます。[注: dcl.init.aggr 集計メンバーが初期化される順序について説明します。ローカル静的オブジェクトの初期化については、stmt.dclで説明されています。]

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

6.7 宣言文 [stmt.dcl]

...

4

静的ストレージ期間 ( basic.stc.static ) を持つすべてのローカル オブジェクトのゼロ初期化 ( dcl.init ) は、他の初期化が行われる前に実行されます。定数式で初期化された静的ストレージ期間を持つPOD 型 ( basic.types ) のローカル オブジェクトは、そのブロックが最初に入力される前に初期化されます。実装は、実装が名前空間スコープ ( basic.start.init)。それ以外の場合、そのようなオブジェクトは、コントロールがその宣言を最初に通過するときに初期化されます。そのようなオブジェクトは、初期化の完了時に初期化されたと見なされます。例外をスローして初期化が終了した場合、初期化は完了していないため、次に制御が宣言に入ったときに再試行されます。オブジェクトの初期化中に制御が (再帰的に) 宣言に再び入った場合、動作は未定義です。【例:

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

--終了例

5

静的ストレージ期間を持つローカル オブジェクトのデストラクタは、変数が構築された場合にのみ実行されます。[注: basic.start.term は、静的ストレージ期間を持つローカル オブジェクトが破棄される順序を記述します。]

于 2008-09-12T12:20:09.697 に答える
28

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

于 2008-09-11T00:41:46.857 に答える
10

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

@Adam: コンパイラによるこの舞台裏のコード注入が、あなたが見た結果の理由です。

于 2008-09-11T01:00:58.823 に答える
6

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

  • 結論 (g++、Windows 環境の場合):
  1. クラスのグローバル変数静的メンバー: コンストラクターは、メイン関数(1)に入る前に呼び出されます。
  2. ローカル静的変数: コンストラクターは、実行が最初にその宣言に到達したときにのみ呼び出されます。
  3. ローカル静的変数が POD タイプの場合、メイン関数(1)に入る前にも初期化されます。POD タイプの例: static int number = 10;

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

#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;
    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 env でテストされた人はいますか?

于 2013-12-03T15:49:16.380 に答える
4

それとも、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 ブロック内で使用します。最初の呼び出しで、変数は初期化されます。次に、最初と次の呼び出しで、その値が (参照によって) 返されます。

于 2008-09-11T05:58:41.620 に答える
3

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

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

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

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

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

于 2008-09-10T23:54:27.237 に答える