我意识到我正在尝试做的事情并不安全。但我只是在做一些测试和图像处理,所以我的重点是速度。
现在这段代码为我提供了 32 位像素值类型的相应字节。
struct Pixel {
unsigned char b,g,r,a;
};
我想检查我是否有一个低于特定值的像素(例如 r, g, b <= 0x10
)。我想我只想用 0x00E0E0E0
条件测试像素位的位与位 | (我这里可能有错误的字节顺序)来获取暗像素。
与其使用这个丑陋的烂摊子 (*((uint32_t*)&pixel))
要获得 32 位 unsigned int 值,我认为应该有一种方法可以设置它,这样我就可以使用 pixel.i
,同时保持使用 pixel.g
引用绿色字节的能力.
我可以这样做吗?这行不通:
struct Pixel {
unsigned char b,g,r,a;
};
union Pixel_u {
Pixel p;
uint32_t bits;
};
我需要编辑我现有的代码来说 pixel.p.g
获得绿色字节。如果我这样做,也会发生同样的情况:
union Pixel {
unsigned char c[4];
uint32_t bits;
};
这也行,但我仍然需要更改所有内容以索引到 c
,这有点难看,但如果我真的需要的话,我可以让它与宏一起工作。
最佳答案
(已编辑) gcc 和 MSVC 都允许“匿名”结构/union ,这可能会解决您的问题。例如:
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:使用了非标准扩展:无名结构/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 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 中,您可以非常巧妙地定义对任何给定类型的 union 转换:
#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)
self 记录,更安全。而且不会太丑陋。所有这些建议都可能编译成相同的代码,因此效率不是问题。另请参阅我的相关答案:How to format a function pointer? .
关于 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 .
关于c - 使用 union 来简化转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2876832/