(Zmieniano) Zarówno GCC i MSVC pozwalają „Anonymous” kodowanym/związki, które mogłyby rozwiązać problem. Na przykład:
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);
daje (na Intel):
04030201
Wymaga to zmienia wszystkie swoje deklaracje z struct Pixel do związkowej Pixel w oryginalnym kodzie. Ale ta wada może być ustalona poprzez:
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);
ta współpracuje również z VC9, z „C4201 ostrzeżenie: rozszerzenia niestandardowe zastosowanie: bezimienny struct/union”. Microsoft wykorzystuje tę sztuczkę, na przykład w:
typedef union {
struct {
DWORD LowPart;
LONG HighPart;
}; // <-- nameless member!
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;
, ale one "oszukują", tłumiąc niechciane ostrzeżenie.
Chociaż powyższe przykłady są w porządku, jeśli użyjesz tej techniki zbyt często, szybko otrzymasz niezmienny kod.Pięć wskazówek, aby dokonać rzeczy jaśniej:
(1) Zmień nazwę bits
coś brzydsze jak union_bits
, aby wyraźnie wskazać coś out-of-the-zwyczajne.
(2) Wróć do brzydkiego obsady PO odrzucony, ale ukryć swoją brzydotę w makro lub w funkcji inline, jak w:
#define BITS(x) (*(uint32_t*)&(x))
Ale to byłoby złamać surowe zasady aliasingu. (Patrz, na przykład, odpowiedź AndreyT za:. C99 strict aliasing rules in C++ (GCC))
(3) Zachowaj oryginalne zaliczane do Pixel, ale do lepszej obsady:
struct Pixel {unsigned char b,g,r,a;} foo;
// ...
printf("%08x\n", ((union {struct Pixel dummy; uint32_t bits;})foo).bits);
(4) Ale to jeszcze brzydsze. Można to naprawić przez 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
Z VC9 lub z gcc za pomocą -pedantic, musisz (nie wykorzystanie tego z gcc --see uwaga na koniec) :
printf("%08x\n", ((CastPixelToBits*)&foo)->bits); // VC9 (not gcc)
(5) Być może preferowane może być makro. W gcc, można zdefiniować obsady Unię do danego rodzaju bardzo starannie:
#define CAST(type, x) (((union {typeof(x) src; type dst;})(x)).dst) // gcc
// ...
printf("%08x\n", CAST(uint32_t, foo));
Z VC9 i innych kompilatorów, nie ma typeof
i może być potrzebne wskaźniki (nie użyć tego z gcc --see uwaga na koniec):
#define CAST(typeof_x, type, x) (((union {typeof_x src; type dst;}*)&(x))->dst)
samodokumentujące i bezpieczniejsze. I nie brzydko . Wszystkie te sugestie będą prawdopodobnie kompilować do identycznego kodu, więc efektywność nie jest problemem. Zobacz także moją powiązaną odpowiedź: How to format a function pointer?.
Ostrzeżenie o gcc: GCC ręczny Wersja 4.3.4 (ale nie wersja 4.3.0) stwierdza, że ten ostatni przykład, z &(x)
jest niezdefiniowane zachowanie. Zobacz http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ i http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.
Rzadko słyszysz słowa "union" i "upraszczaj" w tym samym zdaniu ... –
Uważam, że stosowanie maski 0xE0 nie jest równoważne <= 0x10. W szczególności 0x1F jest większe niż 0x10 i zastosowanie maski da 0x00 –
A twój odrzucony "ten paskudny bałagan" \ * ((uint32_t \ *) i piksel) również złamie zasady ścisłego aliasingu. –