У меня есть несколько вопросов относительно профсоюзов и того, что безопасно. Пожалуйста, проверьте мое понимание. Учитывая союз типа:
union
{
uint16_t ab;
struct
{
uint8_t b;
uint8_t a;
};
};
Например, в C99 и более поздних версиях можно установить ab, а затем прочитать b или a. И затем, если я изменю b, чтение ab позже тоже будет в порядке.
Но в C++ это UB.
Является ли a или b самым значимым байтом (MSB) зависит от порядка байтов машины. Например, здесь a для меня — это старший бит, так как это второе поле структуры, а моя машина имеет прямой порядок байтов. Следовательно, мы не можем использовать объединения, не заботясь о порядке байтов.
Верно ли мое понимание?
Ах, окей, я вижу. Да, я должен был также отметить, что я считаю, что анонимные объединения — это функция C11. Спасибо.
«Поэтому мы не можем использовать союзы, не заботясь о порядке байтов».: игра слов — не единственный вариант использования union. Шаблон использования, который также будет действовать как в ISO C++, так и в C, не зависит от каких-либо деталей представления, таких как порядок байтов. Это характерно только для шаблона каламбура.
Вы также должны пометить этот C++.
@You'reNotARobot «Например, в C99 и более поздних версиях можно установить ab, а затем прочитать b или a. А затем, если я изменю b, чтение ab позже также будет в порядке». --> Да. (если только не безумный компилятор, который дополняет struct.)





Верно ли мое понимание?
На самом деле большинство (если не все) систем будут работать так, как вы описываете в приведенном примере кода. Однако это не гарантируется стандартом C.
Как упомянул пользователь «chux - Reinstate Monica» в комментарии, совместимый компилятор C может добавлять отступы после каждого члена структуры. В таких случаях член структуры a не делит память с членом объединения ab, поэтому при установке ab байт, соответствующий a, принимает неопределенное значение. Вероятно, вы не найдете компилятора, делающего это, но стандарт C позволяет это.
Для вашего конкретного примера массив типа uint8_t x[2]; будет лучше, чем struct, поскольку он решает проблему заполнения.
Хотя в C разрешено писать одному члену объединения, а затем читать другому члену объединения, в общем случае есть несколько вещей, которые делают это совершенно бесполезным.
В вопросе уже упоминается проблема порядка байтов, но это еще не все.
Пример:
union
{
uint16_t ab;
uint8_t b;
} u;
u.ab = 0x0102;
u.b = 0x03;
После этого возникает соблазн предположить, что u.ab теперь содержит либо 0x0302, либо 0x0103 (в зависимости от порядка байтов), но это не указано в стандарте.
В разделе 6.2.6.1 (проект №1570) говорится:
Когда значение сохраняется в члене объекта типа объединения, байты представления объекта, которые не соответствуют этому члену, но соответствуют другим членам, принимают неуказанные значения.
Таким образом, стандарт C допускает, чтобы результатом u.ab в приведенном выше коде был либо 0x03??, либо 0x??03 (в зависимости от порядка байтов). Опять же, вы, вероятно, не найдете компилятора/системы, делающей это, но стандарт C позволяет это.
В C беззнаковые целочисленные типы не могут иметь представления-ловушки, но если ваш союз использует типы, которые имеют представления-ловушки (в какой-то конкретной системе), то запись одного члена объединения может привести к тому, что другой член объединения станет представлением-ловушкой, и его чтение может быть довольно плохим.
Другой пример — типы struct, у которых действительно есть отступы. Значение заполняющих байтов на самом деле невозможно контролировать. Основные операции с элементом struct могут (за вашей спиной) изменить отступы. Если другой член объединения действительно использует те же байты, ситуация выходит из-под контроля.
Могут быть простые примеры, когда безопасно написать один член союза, а затем прочитать другого. Но в общем случае возникает несколько проблем и лучше их избегать. По крайней мере, если вам нужен надежный код.
«Но в CPP вышеприведенное — это UB».: В ISO C++ определение
unionбудет неправильно сформировано, а не UB, поскольку в C++ не существует анонимных членов структуры. На практике компиляторы C++ поддерживают анонимную структуру как расширение языка в нестрогих режимах, а также поддерживают каламбур типов как расширение для совместимости с C, по крайней мере, если типы тривиальны и имеют стандартную компоновку.