объединение против битовой маскировки и битового сдвига

Каковы недостатки использования unions при хранении некоторой информации, такой как последовательность байтов, и возможности доступа к ним сразу или по одному.

Пример: Цвет может быть представлен в RGBA. Таким образом, цветовой тип может быть определен как

typedef unsigned int RGBAColor;

Затем мы можем использовать «сдвиг и маскирование» битов для «получения или установки» значений красного, зеленого, синего и альфа-канала объекта RGBAColor (точно так же, как это делается в функциях Direct3D с помощью макрофункций, таких как D3DCOLOR_ARGB()).

Но что, если я использовал союз,

union RGBAColor
{
unsigned int Color;
struct RGBAColorComponents
{
    unsigned char Red;
    unsigned char Green;
    unsigned char Blue;
    unsigned char Alpha;
} Component;
};

Тогда мне не нужно будет всегда выполнять смещение (<<) или маскирование (&) для чтения или записи компонентов цвета. Но есть ли в этом проблема? (Я подозреваю, что в этом есть какая-то проблема, потому что я не видел, чтобы кто-то использовал такой метод.)

Может ли Endianness быть проблемой? Если мы всегда используем Component для доступа к цветовым компонентам и используем Color для доступа ко всему (для копирования, назначения и т. д. в целом), порядок байтов не должен быть проблемой, верно?

-- РЕДАКТИРОВАТЬ -- Я нашел старый пост с той же проблемой. Так что я думаю, что этот вопрос своего рода репост: P извините за это. вот ссылка: Рекомендуется ли использовать союзы в C++?

Согласно ответам кажется, что использование союзов для данного примера допустимо в C++. Поскольку там нет изменения типа данных, это всего лишь два способа доступа к одним и тем же данным. Пожалуйста, поправьте меня, если я ошибаюсь. Спасибо. :)


person Deamonpog    schedule 07.02.2013    source источник


Ответы (2)


Такое использование объединений недопустимо в C++, где объединение включает перекрывающиеся, но взаимоисключающие объекты. Вы не можете записать одного члена союза, а затем зачитать другого члена.

Это разрешено в C, где это рекомендуемый способ каламбура.

Это относится к проблеме (строгого) алиасинга, которая представляет собой трудность, с которой сталкивается компилятор при попытке определить, различны ли два объекта с разными типами. Языковые стандарты не совпадают, потому что эксперты все еще выясняют, какие гарантии можно безопасно предоставить без ущерба для производительности. Лично я всего этого избегаю. Для чего на самом деле будет использоваться int? Безопасный способ перевода — скопировать байты, например memcpy.

Существует также проблема порядка байтов, но важно ли это, зависит от того, что вы хотите сделать с int.

person Potatoswatter    schedule 07.02.2013
comment
Э, нет. Использование объединений для переинтерпретации данных является полной противоположностью устаревшему, оно было кодифицировано в C11 как стандартизация существующей практики. union { struct foo x; unsigned y[4]; }; в основном является новым предпочтительным способом перетасовки битов между типами, когда вы слишком хороши для memcpy(). Строгие правила псевдонимов — это то, что вы нарушаете, когда используете указатели, например, (unsigned *) &x. И да, я имею в виду новый - только в последнее десятилетие достижения в компиляторах создали проблемы для тех, кто нарушает фактические строгие правила алиасинга. - person Dietrich Epp; 07.02.2013
comment
Итак, если я правильно понимаю, в соответствии с правилом Strict Aliasing проблема заключается в том, что я использую указатели внутри объединения, такие как union { unsigned int * pi; unsigned char * pc; }. И хорошо использовать union { unsigned int val; unsigned char vals[4]; } . (Я не знал об этом правиле Strict Aliasing. Так что извините меня. Я читаю эту статью прямо сейчас.) - person Deamonpog; 07.02.2013
comment
@DietrichEpp Вы уверены? Это может отличаться между C11 и C++11. - person Potatoswatter; 07.02.2013
comment
@Deamonpog: Когда ты делаешь это, ты нарушаешь совершенно другие правила. Не делай этого. Когда вам нужно преобразовать указатель из одного типа в другой, вместо этого используйте приведение типов. - person Dietrich Epp; 07.02.2013
comment
Я уверен, что доступ к неправильному члену union не противоречит строгим правилам псевдонимов. В C это было против другого правила, которое запрещало доступ к членам союзов, отличных от правильного, но это правило было изменено, поэтому метод допустим, но результаты определяются реализацией. - person Dietrich Epp; 07.02.2013
comment
Что касается C++, метод union является методом де-факто для этого именно потому, что (1) он не нарушает строгий алиасинг (2) другие методы нарушают строгий алиасинг (3) если вы нарушите строгий алиасинг, вы наверное столкнулся с проблемами. То есть, предполагая, что вы не можете использовать memcpy() и не выполняете приведение к unsigned char, что в любом случае нормально. - person Dietrich Epp; 07.02.2013
comment
Спасибо. Я на самом деле использую C++. Так что используйте этот метод для цветов. :) - person Deamonpog; 07.02.2013
comment
@DietrichEpp Это и этот ответ показывает, что псевдонимы с помощью union допустимы в C, а не в C++. C++ допускает memcpy, который эффективно инициализирует объект до того, как начнется его время жизни. Но он не позволяет двум объектам одновременно жить в одном и том же месте. - person Potatoswatter; 07.02.2013
comment
@Potatoswatter: Да, это то, что де-факто означает. - person Dietrich Epp; 08.02.2013
comment
@DietrichEpp Если практика де-факто - это UB, то ее все равно нужно изменить. Правила таковы, какими они определены; общественное мнение не имеет значения. - person Potatoswatter; 08.02.2013
comment
@Potatoswatter: я думаю, что это отклоняется от темы. В вашем ответе утверждается, что это противоречит строгим правилам псевдонимов, хотя на самом деле это не противоречит строгим правилам псевдонимов. В вашем ответе утверждается, что такое поведение устарело, без указания стандарта, но это неверно независимо от того, какой стандарт вы выберете. В C++ эту технику нельзя назвать устаревшей, потому что она никогда не была легальной. В C deprecated — это полная противоположность правильному слову, поскольку поведение было UB, но больше не UB. - person Dietrich Epp; 08.02.2013
comment
Обсуждение де-факто должно было расширить тему. Исторически сложилось так, что оба комитета по стандартам считали частью своих полномочий кодификацию существующих практик: они придерживаются точки зрения, что существующий код более ценен, чем существующие реализации. Поэтому причина, по которой я поднял этот вопрос, заключается в том, что я считаю гораздо более вероятным, что будущий стандарт C++ переопределит union трюк с определенным поведением, и несколько менее вероятным, что поставщики компиляторов введут оптимизации, вызывающие нежелательное поведение. Это не математика, стандарт не имеет решающего значения. - person Dietrich Epp; 08.02.2013
comment
@DietrichEpp Спасибо за разъяснение вашего смысла. Это спорный вопрос… - person Potatoswatter; 08.02.2013

Я считаю, что использование объединения решает любые проблемы, связанные с порядком следования байтов, поскольку, скорее всего, порядок RGBA определяется в сетевом порядке. Кроме того, тот факт, что каждый компонент будет иметь тип uint8_t или что-то подобное, может помочь некоторым компиляторам использовать расширенную загрузку со знаком/нулем, сохраняя младшие 8 бит непосредственно в невыровненном указателе байта и даже имея возможность распараллелить некоторые операции с байтами (например, в arm есть несколько упакованных инструкции 4x8 бит).

person Aki Suihkonen    schedule 07.02.2013