Почему некоторые константные переменные, ссылающиеся на некоторые экспортированные константные переменные, получают значение 0?

StackOverflow https://stackoverflow.com/questions/920615

Вопрос

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

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

Это было полезно?

Решение

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

Другие советы

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

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

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

и #include этот файл везде, где он вам нужен.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top