Страницы

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

воскресенье, 5 января 2020 г.

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

#cpp #c #language_lawyer


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

Сколько себя помню, 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++? Отличается ли то, что они говорят?
    


Ответы

Ответ 1



Задача обращатся по одному адресу к данным и как к целой структуре и как к массиву байт - очень часто-встречаемая задача. из комментариев от VTT, что б избежать UB следует делать так. uint8_t color_check(const uint32_t _raw) { uint_8_t * b = reinterpret_cast(&raw) if (b[0] == 0) { return 0; } return 1; } Не следует делать через union - т.к. UB -оптимизатор предполагает что за одну "итерацию" юнион использует одну ветвь, и при включеном оптимизаторе нового поколения программа может отработать не верно. Через каст без указания типа каста. ((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 поля, которое требуется сделать активным.

Ответ 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 ]

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

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