Сначала я объясню, что происходит в вашей реализации.
Вы делаете печатание типа между значением uint64_t
и массивом значений 2 uint32_t
. В соответствии с результатом, ваша система имеет порядок байтов и с радостью принимает этот тип наказания, просто переосмысливая представления байтов. И байтовое представление 0x0a
в порядке байтов uint64_t
:
Byte number 0 1 2 3 4 5 6 7
Value 0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Младший значащий байт в младшем порядке байтов имеет самый низкий адрес. Теперь очевидно, почему представление uint32_t[2]
является { 0x0a, 0x00 }
.
Но то, что вы делаете, разрешено только на языке C.
C язык:
C11 говорит как 6.5.2.3 Структура и члены профсоюза:
3 Постфиксное выражение с последующим. оператор и идентификатор обозначает член
структура или объект объединения. Значение соответствует названному члену 95) и является lvalue, если
первое выражение - lvalue.
В примечании 95) прямо сказано:
Если член, использованный для чтения содержимого объекта объединения, не совпадает с элементом, использованным в последний раз
сохранить значение в объекте, соответствующая часть представления объекта значения переосмысливается
как представление объекта в новом типе , как описано в 6.2.6 (процесс иногда называется ‘‘ типа
каламбурная»»). Это может быть представление ловушки.
Таким образом, даже если примечания не являются нормативными, их цель - прояснить, как должен интерпретироваться стандарт => ваш код действителен и имеет определенное поведение в системе с прямым порядком байтов, определяющей типы uint64_t
и uint32_t
.
C ++ язык:
C ++ более строг в этой части. В проекте n4659 для C ++ 17 в [basic.lval] написано:
8 Если программа пытается получить доступ к сохраненному значению объекта через glvalue, отличный от одного из
Для следующих типов поведение не определено: 56
(8.1) - динамический тип объекта,
(8.2) - cv-квалифицированная версия динамического типа объекта,
(8.3) - тип, подобный (как определено в 7.5) динамическому типу объекта,
(8.4) - тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,
(8.5) - тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа
объекта,
(8.6) - агрегатный или объединенный тип, который включает в себя один из вышеупомянутых типов среди своих элементов или нестатический
элементы данных (включая, рекурсивно, элемент или нестатический элемент данных субагрегата или
содержавший союз),
(8.7) - тип, который является (возможно, квалифицированным по cv) типом базового класса динамического типа объекта,
(8.8) - тип char, unsigned char или std :: byte.
И в примечании 56 прямо говорится:
Цель этого списка - указать те обстоятельства, при которых объект может или не может иметь псевдоним.
Поскольку punning никогда не упоминается в стандарте C ++, а часть struct / union не содержит эквивалента повторной интерпретации C, это означает, что чтение в C ++ значение члена, который не является последним записанным, вызывает неопределенное поведение.
Конечно, обычная реализация компилятора компилирует и C, и C ++, и большинство из них принимает идиому C даже в исходном коде C ++ по той же причине, по которой компилятор gcc C ++ с радостью принимает VLA в исходных файлах C ++. В конце концов, неопределенное поведение включает ожидаемые результаты ... Но вы не должны полагаться на это для переносимого кода.