( Edited ) И gcc, и MSVC допускают «анонимные» структуры / объединения, которые могут решить вашу проблему. Например:
union Pixel {
struct {unsigned char b,g,r,a;};
uint32_t bits; // use 'unsigned' for MSVC
}
foo.b = 1;
foo.g = 2;
foo.r = 3;
foo.a = 4;
printf ("%08x\n", foo.bits);
дает (на Intel):
04030201
Это требует изменения всех ваших объявлений struct Pixel на union Pixel в исходном коде. Но этот дефект можно исправить с помощью:
struct Pixel {
union {
struct {unsigned char b,g,r,a;};
uint32_t bits;
};
} foo;
foo.b = 1;
foo.g = 2;
foo.r = 3;
foo.a = 4;
printf ("%08x\n", foo.bits);
Это также работает с VC9 с предупреждением C4201: используется нестандартное расширение: безымянная структура / объединение. Microsoft использует этот трюк, например, в:
typedef union {
struct {
DWORD LowPart;
LONG HighPart;
}; // <-- nameless member!
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;
но они «обманывают», подавляя нежелательное предупреждение.
Несмотря на то, что приведенные выше примеры хороши, если вы используете эту технику слишком часто, вы быстро получите не поддерживаемый код. Пять предложений, чтобы прояснить ситуацию:
(1) Измените имя bits
на более уродливое, например union_bits
, чтобы четко указать на что-то необычное.
(2) Вернитесь к уродливому приведению отвергнутого OP, но скройте его уродство в макросе или встроенной функции, например:
#define BITS(x) (*(uint32_t*)&(x))
Но это нарушит строгие правила наложения имен. (См., Например, ответ AndreyT: C99 строгие правила алиасинга в C ++ (GCC) .)
(3) Сохраните первоначальное определение Pixel, но сделайте лучший бросок:
struct Pixel {unsigned char b,g,r,a;} foo;
// ...
printf("%08x\n", ((union {struct Pixel dummy; uint32_t bits;})foo).bits);
(4) Но это даже уродливее . Вы можете исправить это с помощью typedef
:
struct Pixel {unsigned char b,g,r,a;} foo;
typedef union {struct Pixel dummy; uint32_t bits;} CastPixelToBits;
// ...
printf("%08x\n", ((CastPixelToBits)foo).bits); // not VC9
С VC9 или с gcc, использующим -pedantic, вам потребуется ( не используйте это с gcc - см. Примечание в конце ) :
printf("%08x\n", ((CastPixelToBits*)&foo)->bits); // VC9 (not gcc)
(5) Возможно, предпочтителен макрос. В gcc вы можете очень точно определить объединение для любого типа:
#define CAST(type, x) (((union {typeof(x) src; type dst;})(x)).dst) // gcc
// ...
printf("%08x\n", CAST(uint32_t, foo));
В VC9 и других компиляторах нет typeof
, и могут потребоваться указатели ( не используйте это с gcc - см. Примечание в конце ):
#define CAST(typeof_x, type, x) (((union {typeof_x src; type dst;}*)&(x))->dst)
Самодокументируемый и безопасный. И не слишком безобразно. Все эти предложения, вероятно, скомпилированы в идентичный код, поэтому эффективность не является проблемой. Смотрите также мой связанный ответ: Как отформатировать указатель функции? .
Предупреждение о gcc: В Руководстве GCC версии 4.3.4 (но не версия 4.3.0) говорится, что в этом последнем примере с &(x)
неопределенное поведение . См http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ и http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.