#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 ]
Комментариев нет:
Отправить комментарий