Страницы

Поиск по вопросам

понедельник, 15 октября 2018 г.

Когда можно нарушать правило о strict-aliasing?

Я знаю, что strict-aliasing - это запрет создания двух и более указателей на одну область памяти как одинаковых типов, так и различных. Действительно, такой код будет иметь неопределённое поведение. Но до недавнего времени (комментарий gbg) я думал, что union помогает использовать поля разных типов, находящихся на одинаковом смещении, без опасности неопределённого поведения. Но, как оказалось, Керниган и Ричи пишут:
Извлекать можно данные только того типа, какие были помещены в объединение при последнем обращении к переменной.
Тогда мне стало интересно, как в библиотечных функциях реализовано побайтное чтение и запись и последующее преобразование этих байт в базовые типы: int, char, float и др. Вот, например, фрагмент исходного кода Qt:
QDataStream &QDataStream::operator>>(float &f) { if (version() >= QDataStream::Qt_4_6 && floatingPointPrecision() == QDataStream::DoublePrecision) { double d; *this >> d; f = d; return *this; } f = 0.0f; CHECK_STREAM_PRECOND(*this) if (dev->read((char *)&f, 4) != 4) { f = 0.0f; setStatus(ReadPastEnd); } else { if (!noswap) { union { float val1; quint32 val2; } x; x.val2 = qbswap(*reinterpret_cast(&f)); f = x.val1; } } return *this; }
Здесь есть как преобразование указателя к другому типу с записью по данному адресу, так чтение из поля объединения, отличного от того, в которое производилась запись. Странно. Почему разработчикам Qt можно нарушать strict-aliasing, а мне нельзя?
UPD. Отдельно хотелось бы узнать, нарушает ли string-aliasing приведение типа void* к любому типу при вызове функции malloc
p.s. Хотелось бы услышать @gbg, @Abyx, так как на их ответы я ссылаюсь.


Ответ

Начать надо с того что вы цитируете Кернигана и Ричи из их книги про C из прошлого столетия, а приводите пример кода и спрашиваете про современные С и C++. Сегодняшние стандарты C99 и C11 разрешают использовать union так как это делают разработчики Qt
If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.
Неопределённое поведение возникает если вы делаете какие-то предположения об устройстве каких-то типов, которое не всегда может быть верным (яблоко и апельсин часто одного размера, но нельзя сказать что это верно вообще для всех яблок).
В данном случае неопределенного поведения не может быть потому что float и quint32 всегда и везде занимают 32 бита. Разработчики Qt заранее позаботились об этом.
Другой вопрос что по стандарту C++ компиляторы не так чтобы прямо обязаны делать всё то, что явно обязаны делать по стандарту C. Вопрос широкий.
Например, по мнению gcc так делать в принципе можно если обращаться к полям по значению. Если же вы надумаете обратиться к значению по ссылке на поле, то ждите беды.

Комментариев нет:

Отправить комментарий