(已编辑)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);
给出(在英特尔上):
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 strict aliasing rules in 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 或使用 -pedantic 的 gcc,您将需要(不要将其与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。