エクスポートされた const 変数を参照する const 変数の値が 0 になるのはなぜですか?
-
06-09-2019 - |
質問
次の点を考慮してください。次のような 2 つのエクスポートされた定数があります。
// somefile.h
extern const double cMyConstDouble;
extern const double cMyConstDouble2;
そして
// somefile.cpp
const double cMyConstDouble = 3.14;
const double cMyConstDouble2 = 2.5*cMyConstDouble;
これらの定数は、2 つの静的 (ローカルに表示される) 定数を定義するために別の場所で参照されるようになりました。
// 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);
これにより、次の出力が得られます。
cAnotherDouble = 3.454, cAnotherDouble2 = 0
2 番目のダブルが 0 なのはなぜですか?.NET 2003 C++ コンパイラ (13.10.3077) を使用しています。
解決
、コンパイラはその値を仮定することができず、cMyConstDouble2のためのコンパイル時の初期化を生成しません。 cMyConstDouble2時間が初期化されコンパイルされていないとして、cAnotherDouble2に初期化に対してその順序はランダムである(不定)。多くのための静的初期の大失敗を参照してください。情報ます。
他のヒント
私はここにexternの問題に私のつま先を浸漬するつもりはないが、なぜあなたは、単に適切なヘッダファイルにconstsを配置し、それらをexternを使用して「エクスポート」を忘れてませんか?これはconstsは、C ++で使用されることになって、そしてなぜ彼らは内部リンケージを持っている方法です。
言い換えるます:
// someheader.h
const double cMyConstDouble = 3.14;
const double cMyConstDouble2 = 2.5*cMyConstDouble;
とあなたがそれらを必要な場所にそのファイルを#includeます。
これは、1つのソースファイル内の1つの静的変数として行うには危険な事は別のcppファイル内の別の静的変数に依存しています。詳細については、静的初期の大失敗を確認してください。
の初期化を変更すると、 cMyConstDouble2
ここでこれに:
const double cMyConstDouble2 = 2.5*3.14;
そうすれば、プログラムは正しく動作するはずです。この理由は、変数が
- PODタイプがある
- 定数式で初期化されます (1)
静的初期化時に初期化されます。これらの初期化には以下が含まれます
- のゼロ初期化 全て 静的ストレージ期間を持つオブジェクト
- 定数式で初期化された POD の初期化
表示された変数のうち、 cMyConstDouble
静的初期化時に完全に初期化されるという両方の条件を満たします。しかし、 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
動的初期化によって他の静的ストレージ変数が変更されない場合 ( あなたの コード)、および静的初期化が動的初期化によって生成される値と同じ値を生成する場合、静的に初期化する必要のないすべてのオブジェクトが動的に初期化される場合(また、 あなたの code) - 変数を静的に初期化できるようになります。これら 2 つの条件は、上記のコードの両方の変数でも満たされています。 d2
そして d1
:
の分析 d2
= d1
他の静的ストレージ変数は変更しません- 両方のとき
d2
そしてd1
動的に初期化されると、d2
に初期化されます0.0
, 、 なぜならd2
前に定義されていますd1
, 、および動的初期化d2
の値を取得しますd1
静的初期化直後の状態 (ゼロ初期化のみ)d1
開催されました)。
の分析 d1
= fd()
他の静的ストレージ変数は変更しません- 両方のとき
d2
そしてd1
動的に初期化されると、= fd()
初期化しますd1
に1.0
.
したがって、コンパイラは初期化する可能性があります d1
静的に 1.0
, なぜなら、optional-static-initialization の両方の条件が満たされているからです。
もし コンパイラは初期化を決定します
d1
そしてd2
動的に、その後d2
に初期化されます0.0
, の値を取得するため、d1
ゼロ初期化直後だったので。しかし, もし コンパイラは初期化を決定します
d1
静的に、そしてd2
動的に、その後d2
に初期化されます1.0
, の動的初期化以来、d2
完全に初期化された値を取得しますd1
静的初期化直後のように。
どれくらいの価値があるのか分かりません d2
いつ d1
そして d2
ただし、静的に初期化されます。つまり、 d2
を掴むことになっている 0.0
または 1.0
, 静的初期化には順序が定義されていないためです。
(1) 静的ストレージ期間を持つオブジェクトの初期化順序を考慮する場合、定数式には (整数定数式だけでなく) 算術定数式も含まれます。