Чтение и запись членов union

109
10 ноября 2019, 10:50

Никак не могу найти однозначный ответ на следующий вопрос.

Сколько себя помню, union-ы всегда использовались не столько для поочередного хранения разных данных в одном месте, сколько для гибкого доступа к некоторым кускам этих самых данных. Иначе говоря, для записи данных одного типа и чтения данных другого типа.

Например:

typedef union u_color_pack
{
    uint8_t b[4];
    uint32_t raw;
} color_pack;
// …
uint8_t color_check(const uint32_t _raw)
{
    color_pack color;
    color.raw = _raw;
    if (color.b[0] == 0)
    {
        return 0;
    } 
    return 1;
}

color_pack позволяет работать с цветом как с сырыми данными типа uint32_t или же напрямую обращаться к отдельным байтам (цветам). Да, я понимаю, что приведенный код зависит от порядка байтов, но такой код обычно пишется с расчетом на строго определенный порядок байтов.

Так вот, проблема в том, что некоторые говорят, что нельзя читать из union-а данные типа A, если перед этим в union были записаны данные типа B.

Одни говорят, что такая ситуация является неопределенным поведением. Другие - что это поведение, определяемое реализацией.

Что по этой проблеме говорят стандарты C и C++? Отличается ли то, что они говорят?

Answer 1

Задача обращатся по одному адресу к данным и как к целой структуре и как к массиву байт - очень часто-встречаемая задача.

из комментариев от VTT, что б избежать UB следует делать так.

uint8_t color_check(const uint32_t _raw)
{
   uint_8_t * b = reinterpret_cast<uint8_t *>(&raw)
   if (b[0] == 0)
   {
      return 0;
   } 
  return 1;
}

Не следует делать через

  1. union - т.к. UB -оптимизатор предполагает что за одну "итерацию" юнион использует одну ветвь, и при включеном оптимизаторе нового поколения программа может отработать не верно.
  2. Через каст без указания типа каста. ((char*)&_raw)[0] - можно поймать UB.

Про порядок байт - порядок байт может меняться, только если используются нестандартные платформы, т.е. если вы предполагаете, что код будет работать на платформах отличных от intel x86/x64-совместимых (или там AMD). Для ускорения вычислений - делают предопределение #define и назначают значение предпроцессору, например

#ifdef litte_indian
   // Прямой порядок
#else
   // Обратный порядок
#endif

Теперь касательно стандартов.

На счёт UB https://habr.com/ru/post/216189/ п 1.3.12

Неопределенное поведение (undefined behavior)– поведение, которое может возникать в результате использования ошибочных программных конструкций или некорректных данных, на которые Международный Стандарт не налагает никаких требований. Неопределенное поведение также может возникать в ситуациях, не описанных в Стандарте явно.

На счёт использования union, стандарт с не оговаривает как правильно использовать юнион, а стандарт c++ вам уже ответили VTT п 12.3

В с++ union в любой момент времени может быть активно не более одного поля. За исключением доступа к общей подструктуре standard-layout объектов, доступ к неактивным полям является неопределенным поведением. В общем случае для обращения к неактивному полю сначала следует вручную вызвать деструктор активного поля, затем вызвать placement new поля, которое требуется сделать активным.

Answer 2

В С++ в union в любой момент времени может быть активно не более одного поля. За исключением доступа к общей подструктуре standard-layout объектов, доступ к неактивным полям является неопределенным поведением. В общем случае для обращения к неактивному полю сначала следует вручную вызвать деструктор активного поля, затем вызвать placement new поля, которое требуется сделать активным.

12.3 Unions [class.union]
1 In a union, a non-static data member is active if its name refers to an object whose lifetime has begun and has not ended (6.8). At most one of the non-static data members of an object of union type can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (12.2), and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members; see 12.2. —end note ]

READ ALSO
С++/WinAPI: GetOpenFileName крашит программу

С++/WinAPI: GetOpenFileName крашит программу

При выполнении GetOpenFileName иногда крашит программу с ошибкой:

171
Убрать специализацию шаблона

Убрать специализацию шаблона

Навеяно вопросом о vector<bool>

123