Ответ 1
(Отредактировано). Как 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: используется нестандартное расширение: nameless struct/union. 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 строгие правила псевдонимов в С++ (GCC).)
(3) Сохраните исходное определение пикселя, но сделайте лучший снимок:
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)
является undefined. См. http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ и http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.