Почему некоторые константные переменные, ссылающиеся на некоторые экспортированные константные переменные, получают значение 0? - PullRequest
2 голосов
/ 28 мая 2009

Учтите следующее. У меня есть две экспортированные константы следующим образом:

// 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 ]

9 голосов
/ 28 мая 2009

Я не собираюсь рассказывать здесь о проблемах extern, но почему вы просто не помещаете константы в соответствующие заголовочные файлы и не забываете об «экспорте» их с помощью extern? Вот как conts предполагается использовать в C ++ и почему они имеют внутреннюю связь.

Другими словами:

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

и # включайте этот файл, где бы он вам не нужен.

8 голосов
/ 28 мая 2009

Поскольку cMyConstDouble объявлен как extern, компилятор не может принять его значение и не генерирует инициализацию времени компиляции для cMyConstDouble2. Поскольку cMyConstDouble2 не инициализируется во время компиляции, его порядок инициализации относительно cAnotherDouble2 является случайным (не определено). См. статическая инициализация фиаско для получения дополнительной информации.

3 голосов
/ 28 мая 2009

Это опасная вещь, поскольку ваша одна статическая переменная в одном исходном файле зависит от другой статической переменной в другом файле cpp. Проверьте статическая инициализация фиаско для получения дополнительной информации.

2 голосов
/ 28 мая 2009

Если вы измените инициализацию 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

Если динамическая инициализация не изменяет никакую другую статическую переменную хранения (удовлетворяется в вашем коде) и когда статическая инициализация выдаст то же значение, что и динамическая инициализация, когда все объекты не требуются статическая инициализация будет динамически инициализироваться (также выполняется в вашем коде) - тогда переменная может быть инициализирована статически. Эти два условия также выполняются в приведенном выше коде для обеих переменных 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, потому что оба условия для необязательной статической инициализации выполнены.

  • Если компилятор решит инициализировать d1 и d2 динамически, то d2 будет инициализирован до 0.0, так как он захватит значение d1 как было сразу после нулевой инициализации.

  • Однако , , если компилятор решит инициализировать d1 статически и d2 динамически, тогда d2 будет инициализирован до 1.0 , поскольку динамическая инициализация d2 будет захватывать полностью инициализированное значение d1, как это было сразу после статической инициализации.

Я не уверен, каково значение d2, когда d1 и d2 статически инициализируются, однако. То есть, предполагается ли d2 захватить 0.0 или 1.0, поскольку для статической инициализации не определен порядок.


(1) Выражения констант также включают выражения арифметических констант (не только целочисленные выражения констант) при рассмотрении порядка инициализации объектов со статической продолжительностью хранения.

...