(Modificato) Sia gcc che MSVC consentono di creare strutture/unioni "anonime", che potrebbero risolvere il problema. Per esempio:
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);
dà (su Intel):
04030201
Ciò richiede la modifica tutte le dichiarazioni di struct Pixel a Pixel unione nel codice originale. Ma questo difetto può essere fissato tramite:
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);
Questo funziona anche con VC9, con 'C4201 di avviso: estensione non standard utilizzata: senza nome struct/union'. Microsoft utilizza questo trucco, per esempio, in:
typedef union {
struct {
DWORD LowPart;
LONG HighPart;
}; // <-- nameless member!
struct {
DWORD LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;
ma 'imbrogliare' sopprimendo l'avvertimento indesiderato.
Mentre gli esempi precedenti sono ok, se si utilizza questa tecnica troppo spesso, si finisce rapidamente con il codice non gestibile.Cinque suggerimenti per rendere le cose più chiare:
(1) Modificare il nome bits
in qualcosa di più brutto come union_bits
, per indicare chiaramente qualcosa di straordinario.
(2) Torna il brutto cast OP ha respinto, ma nascondere la sua bruttezza in una macro o in una funzione inline, come in:
#define BITS(x) (*(uint32_t*)&(x))
Ma questo avrebbe rotto le rigide regole di aliasing. (Si veda, ad esempio, la risposta di AndreyT:. C99 strict aliasing rules in C++ (GCC))
(3) Tenere il definiton originale di Pixel, ma fare un cast meglio:
struct Pixel {unsigned char b,g,r,a;} foo;
// ...
printf("%08x\n", ((union {struct Pixel dummy; uint32_t bits;})foo).bits);
(4) Ma questo è anche più brutto. È possibile risolvere questo problema da un 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
Con VC9, o con gcc utilizzando -pedantic, è necessario (non uso questo con gcc --see la nota alla fine) :
printf("%08x\n", ((CastPixelToBits*)&foo)->bits); // VC9 (not gcc)
(5) Può essere preferibile una macro. In gcc, è possibile definire un cast sindacato per un determinato tipo molto ordinatamente:
#define CAST(type, x) (((union {typeof(x) src; type dst;})(x)).dst) // gcc
// ...
printf("%08x\n", CAST(uint32_t, foo));
Con VC9 e altri compilatori, non c'è typeof
, e puntatori potrebbe essere necessario (no uso questo con gcc nota --see alla fine):
#define CAST(typeof_x, type, x) (((union {typeof_x src; type dst;}*)&(x))->dst)
auto-documentazione, e più sicuro. E non anche brutto. È probabile che tutti questi suggerimenti vengano compilati con codice identico, quindi l'efficienza non è un problema. Vedi anche la mia risposta correlata: How to format a function pointer?.
Attenzione riguardo gcc: Manuale Il GCC versione 4.3.4 (ma non versione 4.3.0) afferma che quest'ultimo esempio, con &(x)
, è comportamento indefinito. Vedi http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ e http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.
Raramente si sentono le parole "unione" e "semplificazione" nella stessa frase ... –
Credo che applicare la maschera 0xE0 non sia equivalente a <= 0x10. In particolare 0x1F è maggiore di 0x10 e l'applicazione della maschera produrrà 0x00 –
E il tuo rifiuto 'questo brutto pasticcio' \ * ((uint32_t \ *) e pixel) infrangerebbe anche le rigide regole di aliasing. –