为什么一些引用某些导出的const变量的const变量的值为0?

时间:2009-05-28 12:35:53

标签: c++ initialization const double extern

请考虑以下事项。我有两个导出的常量如下:

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

// 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);

产生以下输出:

cAnotherDouble = 3.454, cAnotherDouble2 = 0

为什么第二个双0?我正在使用.NET 2003 C ++编译器(13.10.3077)。

4 个答案:

答案 0 :(得分:9)

我不打算在这里讨论extern的问题,但是为什么你不把consts放在适当的头文件中而忘记使用extern“导出”它们?这就是C ++中应该使用consts的方式,以及为什么它们具有内部链接。

换句话说:

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

和#include该文件,只要你需要它们。

答案 1 :(得分:8)

由于cMyConstDouble声明为extern,因此编译器无法假定其值,也不会为cMyConstDouble2生成编译时初始化。由于cMyConstDouble2未初始化编译时间,因此其相对于cAnotherDouble2的初始化顺序是随机的(未定义)。有关详细信息,请参阅static initialization fiasco

答案 2 :(得分:3)

这是危险的事情,因为一个源文件中的一个静态变量依赖于另一个cpp文件中的另一个静态变量。查看static initialization fiasco了解详情。

答案 3 :(得分:2)

如果您将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

如果动态初始化不会更改任何其他静态存储变量(在您的代码中满足),并且静态初始化将产生与动态初始化时所产生的值相同的值需要静态初始化的内容将动态初始化(也在您的代码中得到满足) - 然后允许变量静态初始化。对于变量d2d1

,上述代码也满足这两个条件

分析d2

  • = d1不会更改任何其他静态存储变量
  • 当动态初始化d2d1时,d2会初始化为0.0,因为在d2之前定义了d1并且d2的动态初始化将从静态初始化之后的状态(其中仅发生d1的零初始化)获取d1的值。

分析d1

  • = fd()不会更改任何其他静态存储变量
  • 当动态初始化d2d1时,= fd()会将d1初始化为1.0

因此,编译器可能会将d1静态初始化为1.0,因为满足了可选静态初始化的两个条件。

  • 如果,编译器决定动态初始化d1d2,则 d2将初始化为0.0 ,因为它会在零初始化之后获取d1的值。

  • 然而如果 ,则编译器决定动态初始化d1并动态d2,然后 {{1} }将被初始化为d2 ,因为1.0的动态初始化将获取d2的完全初始化值,因为它刚好在静态初始化之后。

但是,当d1 d2静态初始化时,我不确定d1的价值是多少。也就是说,d2是否应该抓取d20.0,因为没有为静态初始化定义订单。


(1)当考虑具有静态存储持续时间的对象的初始化顺序时,常量表达式也包括算术常量表达式(不仅是整数常量表达式)。