2009-05-28 4 views
2

以下を考慮してください。エクスポートされたconst変数を参照するconst変数のなかで、値が0になるのはなぜですか?

// somefile.h 
extern const double cMyConstDouble; 
extern const double cMyConstDouble2; 

とこれらの定数は現在2静的(ローカルに見える)の定数を定義するために、他のいくつかの場所を参照され

// somefile.cpp 
const double cMyConstDouble = 3.14; 
const double cMyConstDouble2 = 2.5*cMyConstDouble; 

// someotherfile.cpp 
#include "somefile.h" 
static const double cAnotherDouble = 1.1*cMyConstDouble; 
static const double cAnotherDouble2 = 1.1*cMyConstDouble2; 
printf("cAnotherDouble = %g, cAnotherDouble2 = %g\n", 
     cAnotherDouble, cAnotherDouble2); 

利回りを次のように私は2つのエクスポートされた定数を持っています次の出力:

cAnotherDouble = 3.454, cAnotherDouble2 = 0 

なぜ2番目のdoubleは0ですか?私は.NET 2003 C++コンパイラ(13.10.3077)を使用しています。

答えて

8

cMyConstDoubleがexternとして宣言されているため、コンパイラはその値を引き継ぐことができず、cMyConstDouble2のコンパイル時の初期化を生成しません。 cMyConstDouble2は初期化されたコンパイル時ではないので、cAnotherDouble2に対する初期化の順序はランダム(未定義)です。詳細については、static initialization fiascoを参照してください。

+0

@Suma: 'cMyConstDouble2'が' cMyConstDouble'が 'extern'のために定数フォールドされていない場合、' cAnotherDouble'と同じことは起こりませんか?これも 'cMyConstDouble'の権利に依存していますか? 3.454 – legends2k

+0

モジュール間の初期化の順序は定義されていません。あるケースではすでに完了していて、2番目のケースではなかった。 – Suma

9

私はここでexternの問題に触れませんが、なぜ適切なヘッダファイルにconstを配置してexternを使って "export"するのを忘れるのですか?これは、constがC++でどのように使用されているのか、なぜそれらは内部リンケージを持つのでしょうか。言い換えれば

// someheader.h 
const double cMyConstDouble = 3.14; 
const double cMyConstDouble2 = 2.5*cMyConstDouble; 

とあなたがそれらを必要な場所にそのファイルを#include。

+0

だから、あなたは*あなたのつま先をつかんでいますか?とにかく:-) +1。 – paxdiablo

+0

本当に - 私のソリューションはexternを使用していません。 –

+1

実際に理由があります。私たちはあなたの道からこのように移動しました。 – ralphtheninja

3

これは、あるソースファイルの静的変数が別の静的変数に依存するため危険です。詳細については、static initialization fiascoを参照してください。

+0

一般的に危険ですが、定数によるコンパイル時の初期化が行われている場合は適用されません。私の答えは、コンパイル時の初期化のように見えるかもしれませんが、実際はexternのためではありません。 – Suma

2

あなたはここにこれにcMyConstDouble2の初期設定を変更する場合:

const double cMyConstDouble2 = 2.5*3.14; 

を次に、あなたのプログラムが正しい振る舞うべき。この理由は、

  • てきたPODタイプ
  • は定数式で初期化されていることの変数は(1)

が静的​​初期化時に初期化されていることです。これらの初期化は

  • ゼロ初期化のすべてオブジェクトは、静的記憶域期間定数式
あなたの示す変数の

、唯一cMyConstDouble満たす完全に初期化されているの両方の条件を使用して初期化のPODの

  • 初期化を有するものが挙げられます静的初期化時にただし、cMyConstDouble2は、初期化子が定数式の要件を満たしていないため、使用しません。特に、整数型(ここでは浮動小数点型)を持たない変数が含まれています。ただし、浮動小数点リテラルは、算術定数式ではです。そのため、2.5*3.14は算術定数式です。そのため、イニシャライザを静的に初期化する必要があります。


    非定数式にとどまるとcMyConstDouble2はどうなりますか?答えはあなたが知らないということです。標準では、変数を静的に初期化することができますが、その必要はありません。あなたのケースでは、それは動的に初期化されました - したがって静的初期化時間の直後の値はまだゼロでした。どのように複雑のための感覚を得るために、ここでは一例です:

    inline double fd() { return 1.0; } 
    extern double d1; 
    double d2 = d1; // unspecified: 
           // may be statically initialized to 0.0 or 
           // dynamically initialized to 1.0 
    double d1 = fd(); // may be initialized statically to 1.0 
    

    動的初期化は、(あなたコードで満足)とするとき、静的な初期化の他の静的格納変数を変更しない場合静的に初期化される必要のないすべてのオブジェクトが動的に初期化されるときに動的初期化によって生成されるのと同じ値を生成します(コードでも満たされます)。 - 変数は静的に初期化できます。これら2つの条件が両方の変数d2d1ための上記のコードでも満足している:

    両方d2d1が動的に初期化される場合d2

    • = d1の分析は、他の静的記憶変数
    • を変更しませんd2は、d2d1の前に定義されているため、d20.0に初期化され、d2の動的初期化はgr ab d1の値は、静的初期化直後の状態(d1のゼロ初期化のみが行われた場合)になります。 d1

      • = fd()

      分析の両方d2d1が動的に初期化されると、次に= fd()1.0からd1を初期化する

    • 他の静的記憶変数を変更しません。

    したがって、オプションの静的初期化の両方の条件が満たされているため、コンパイラはd1を静的に1.0に初期化することがあります。

    • コンパイラは動的d1d2を初期化することを決定した場合、それは単にゼロで初期化した後だったので、それはd1の値を取得するので、その後、d2は、0.0に初期化されます。しかしながら

    • 場合、コンパイラは、それがあったとしてd2の動的初期化d1の完全に初期化値を取得するので、静的d1と動的、次いでd21.0に初期化されるd2を初期化することを決定します静的初期化の直後である。

    私はd1d2はいえ、静的に初期化されるときにd2の値が何であるかわかりません。つまり、d20.0または1.0を取得するはずです。これは、静的初期化の順序が定義されていないためです。


    静的記憶域期間を持つオブジェクトの初期化順序を考慮すると、(1)定数式は、(積分定数式のみならず)余りに算術定数式が挙げられます。

  • 関連する問題