Союзы UB и порядок байтов

У меня есть несколько вопросов относительно профсоюзов и того, что безопасно. Пожалуйста, проверьте мое понимание. Учитывая союз типа:

union
{
    uint16_t ab;
    struct
    {
        uint8_t b;
        uint8_t a;
    };
};
  • Например, в C99 и более поздних версиях можно установить ab, а затем прочитать b или a. И затем, если я изменю b, чтение ab позже тоже будет в порядке.

  • Но в C++ это UB.

  • Является ли a или b самым значимым байтом (MSB) зависит от порядка байтов машины. Например, здесь a для меня — это старший бит, так как это второе поле структуры, а моя машина имеет прямой порядок байтов. Следовательно, мы не можем использовать объединения, не заботясь о порядке байтов.

Верно ли мое понимание?

«Но в CPP вышеприведенное — это UB».: В ISO C++ определение union будет неправильно сформировано, а не UB, поскольку в C++ не существует анонимных членов структуры. На практике компиляторы C++ поддерживают анонимную структуру как расширение языка в нестрогих режимах, а также поддерживают каламбур типов как расширение для совместимости с C, по крайней мере, если типы тривиальны и имеют стандартную компоновку.

user17732522 29.06.2024 01:00

Ах, окей, я вижу. Да, я должен был также отметить, что я считаю, что анонимные объединения — это функция C11. Спасибо.

You'reNotARobot 29.06.2024 01:02

«Поэтому мы не можем использовать союзы, не заботясь о порядке байтов».: игра слов — не единственный вариант использования union. Шаблон использования, который также будет действовать как в ISO C++, так и в C, не зависит от каких-либо деталей представления, таких как порядок байтов. Это характерно только для шаблона каламбура.

user17732522 29.06.2024 01:04

Вы также должны пометить этот C++.

Allan Wind 29.06.2024 04:17

@You'reNotARobot «Например, в C99 и более поздних версиях можно установить ab, а затем прочитать b или a. А затем, если я изменю b, чтение ab позже также будет в порядке». --> Да. (если только не безумный компилятор, который дополняет struct.)

chux - Reinstate Monica 29.06.2024 04:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Верно ли мое понимание?

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

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

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