Почему элемент `float x` инициализируется `0.` для объектов `a` и `b` в main()?

Может ли кто-нибудь указать, какой пункт в стандарте поддерживает следующее поведение, полученное в Coliru, для фрагмента:

#include <iostream>

class A
{
    int i;
    float x;

    public:
    A() : i(10) {}
    A(int i) : i(i) {}
    int GetI() { return i; }
    float GetF() { return x; }
};


int main()
{
    A a;
    A b(1);
    A x{};
    A y{1};
    std::cout << a.GetI() << '\n';
    std::cout << a.GetF() << '\n';
    std::cout << b.GetI() << '\n';
    std::cout << b.GetF() << '\n';
    std::cout << x.GetI() << '\n';
    std::cout << x.GetF() << '\n';
    std::cout << y.GetI() << '\n';
    std::cout << y.GetF() << '\n';
}

Код печатает:

10
0 ‹-- Не должно быть неизвестно?
1
0 ‹-- idem
10
0
1
0

Изменить:

Этот абзац взят из 4-го издания TCPL, стр. 490:

Для этого правила не так чисты, как хотелось бы. Для статически размещенных объектов (§6.4.2) правила точно такие же, как если бы вы использовали {}, поэтому значение альфы равно {"","",0}. Однако для локальных переменных и объектов бесплатного хранилища инициализация по умолчанию выполняется только для членов типа класса, а члены встроенного типа остаются неинициализированными, поэтому значение бета равно {"","",unknown}.

Г-н Страуструп ничего не говорит о неопределенном поведении.


person Wake up Brazil    schedule 24.12.2013    source источник
comment
Чтение неинициализированной переменной является поведением undefined. Все может случиться, включая (но не ограничиваясь) оценку нуля.   -  person Mat    schedule 25.12.2013
comment
Shouldn't be unknown? Что вы ожидаете напечатать?   -  person Andy Prowl    schedule 25.12.2013
comment
См. этот ответ.   -  person Elliott Frisch    schedule 25.12.2013
comment
В реальном мире вполне вероятно, что значения неинициализированных переменных, просто обнуляющие во вновь запущенном процессе, поскольку современные операционные системы обычно обнуляют страницы памяти перед передачей их приложениям из соображений безопасности.   -  person JohannesD    schedule 25.12.2013
comment
0 — совершенно правильное неизвестное число, так же как 4 — абсолютно правильное случайное число.   -  person Boann    schedule 25.12.2013


Ответы (4)


0 — одно из возможных произвольных значений, которые может получить неинициализированная переменная: программа имеет неопределенное поведение. Учитывая, что существует большая вероятность того, что память инициализируется с нуля, 0 является вероятным результатом: представление IEEE 754 для 0 оказывается полностью нулевым. Однако нет гарантии, что значение будет 0.

person Dietmar Kühl    schedule 24.12.2013
comment
Впрочем, это даже не случайно. Это просто неправильно. - person tmyklebu; 25.12.2013
comment
См. мое редактирование выше о неопределенном поведении. - person Wake up Brazil; 25.12.2013
comment
@WakeupBrazil: вместо этого см. стандарт о неопределенном поведении. - person tmyklebu; 25.12.2013
comment
@tmyklebu Как я уже сказал выше, я хотел бы посмотреть, где в Стандарте говорится что-либо об этом конкретном случае. - person Wake up Brazil; 25.12.2013
comment
Некоторые реализации всегда инициализируют неинициализированное автоматическое значение 0 или это всегда происходит случайным образом (независимо от того, что находится в стеке в выбранной ячейке памяти)? - person Fiddling Bits; 25.12.2013
comment
@WakeupBrazil: соответствующий пункт о чтении неинициализированных значений, я думаю, 4.1 [conv.lval], параграф 1: ... Если объект, на который ссылается glvalue, не является объектом типа T и не является объектом типа полученный из T, или если объект не инициализирован, программа, которая требует этого преобразования, имеет неопределенное поведение. .... - person Dietmar Kühl; 25.12.2013
comment
@FiddlingBits: большинство UNIX начинают с нуля инициализированных страниц. Я не знаю ни одной реализации, которая инициализирует память до нуля. Я уверен, что VC++ можно перевести в режим, в котором он заполняет неинициализированную память известным шаблоном (0xdeadbeef или что-то в этом роде), но я беспокоюсь только об этой второй руке. - person Dietmar Kühl; 25.12.2013
comment
@DietmarKühl Но в выражении A a; нет преобразования lvalue-to-rvalue - person Wake up Brazil; 25.12.2013
comment
@WakeupBrazil: я не утверждал, что есть. Однако в std::cout << a.getF(); есть преобразование lvalue-to-rvalue: вы получаете неопределенное поведение, когда смотрите на неинициализированные данные. - person Dietmar Kühl; 25.12.2013
comment
@DietmarKühl На самом деле мы используем файл cookie 0xDEADBEEF в одном из наших продуктов. Он находится в неинициализированной оперативной памяти. Если отключение питания достаточно короткое, файл cookie сохраняется, в противном случае он теряется, и мы знаем, что отключение питания не было ложным. - person Fiddling Bits; 25.12.2013
comment
@DietmarKühl Большинство UNIX начинаются с нулевых инициализированных страниц, я не знаком с UNIX. Таким образом, я не ожидал значения 0 в стеке. Но после попытки напечатать унитаризованное int в приведенном выше коде я также получил 0, хотя и с предупреждением. Я отдаю вам должное за ответ. спасибо - person Wake up Brazil; 25.12.2013
comment
Правильнее было бы назвать его произвольным, а не случайным (последнее слово имеет особое значение). - person Keith Thompson; 25.12.2013
comment
@KeithThompson: я изменил термин в ответе. Спасибо! - person Dietmar Kühl; 25.12.2013

В Мэт указал, что чтение неинициализированной переменной в C++ в неопределенном поведении означает, что все может (и, вероятно, произойдет в какой-то системе) произойти.

В зависимости от вашего компилятора и конфигурации его сборки вы получите случайное значение x. В вашем случае это 0,0, но это может быть любое случайное значение, в зависимости от того, какие данные оказались в той области памяти, в которой оказался x.

В C++ обычно считается очень плохой практикой не инициализировать переменные-члены в конструкторе, особенно если вы не можете гарантировать, что они будут инициализированы до того, как они будут прочитаны.

person Timo Geusch    schedule 24.12.2013
comment
См. мое редактирование выше о неопределенном поведении. - person Wake up Brazil; 25.12.2013

Что именно вы ожидаете от слова «неизвестно»? 0 — вполне разумное значение для неинициализированной переменной с неопределенным значением. Это просто чистая случайность.

Важно то, что float не был не инициализирован, а в цитируемом вами отрывке БС не говорит о неопределенном поведении, потому что он не ссылается на синтаксис, который вы спрашивая, а вместо этого об агрегатной инициализации (и ее вариантах).

person Lightness Races in Orbit    schedule 24.12.2013

Это хуже, чем просто произвольное значение. Даже если предположить, что ваш компилятор просто использует значение, оставшееся в памяти, и после этого нормально работает с переменной, ранее существовавшие биты могут представлять значение ловушки с плавающей запятой и завершать вашу программу при первом доступе.

Но дело даже не в этом. Поскольку стандарт C++ определяет преобразование lvalue->rvalue для неинициализированных данных как неопределенное поведение, компилятору вполне разрешено делать с вашим кодом все, даже тот код, который не связан с этой переменной< /em>, и даже до первого доступа к этой переменной (пока это может доказать, что грядет неопределенное чтение переменной, что обычно легко доказать только в пределах одного и того же базового блока).

person Ben Voigt    schedule 25.12.2013