使用联合来简化强制转换
我意识到我想做的事情并不安全。但我只是在做一些测试和图像处理,所以我的重点是速度。
现在,这段代码为我提供了 32 位像素值类型的相应字节。
struct Pixel {
unsigned char b,g,r,a;
};
我想检查是否有一个像素低于某个值(例如r, g, b <= 0x10
)。我想我只想用 0x00E0E0E0
对像素的位和位进行条件测试(这里可能有错误的字节序)以获得暗像素。
我认为应该有一种方法来设置它,而不是使用这个丑陋的混乱 (*((uint32_t*)&pixel))
来获取 32 位无符号 int 值,所以我可以只使用pixel.i
,同时保留使用pixel.g
引用绿色字节的能力。
我可以这样做吗?这是行不通的:
struct Pixel {
unsigned char b,g,r,a;
};
union Pixel_u {
Pixel p;
uint32_t bits;
};
我需要编辑现有的代码来表示 pixel.pg
来获取绿色字节。如果我这样做,也会发生同样的情况:
union Pixel {
unsigned char c[4];
uint32_t bits;
};
这也可以,但我仍然需要将所有内容更改为索引到 c
中,这有点难看,但如果我真的需要的话,我可以让它与宏一起工作。
I realize that what I am trying to do isn't safe. But I am just doing some testing and image processing so my focus here is on speed.
Right now this code gives me the corresponding bytes for a 32-bit pixel value type.
struct Pixel {
unsigned char b,g,r,a;
};
I wanted to check if I have a pixel that is under a certain value (e.g. r, g, b <= 0x10
). I figured I wanted to just conditional-test the bit-and of the bits of the pixel with 0x00E0E0E0
(I could have wrong endianness here) to get the dark pixels.
Rather than using this ugly mess (*((uint32_t*)&pixel))
to get the 32-bit unsigned int value, i figured there should be a way for me to set it up so I can just use pixel.i
, while keeping the ability to reference the green byte using pixel.g
.
Can I do this? This won't work:
struct Pixel {
unsigned char b,g,r,a;
};
union Pixel_u {
Pixel p;
uint32_t bits;
};
I would need to edit my existing code to say pixel.p.g
to get the green color byte. Same happens if I do this:
union Pixel {
unsigned char c[4];
uint32_t bits;
};
This would work too but I still need to change everything to index into c
, which is a bit ugly but I can make it work with a macro if i really needed to.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
(已编辑)gcc 和 MSVC 都允许“匿名”结构/联合,这可能会解决您的问题。例如:
给出(在 Intel 上):
这需要将所有 struct Pixel 声明更改为 union Pixel你的原始代码。但这个缺陷可以通过以下方式修复:
这也适用于 VC9,带有“警告 C4201:使用非标准扩展:无名结构/联合”。例如,微软使用了这个技巧:
但他们通过抑制不需要的警告来“作弊”。
虽然上面的示例没问题,但如果您过于频繁地使用此技术,您很快就会得到无法维护的代码。为了让事情变得更清晰,有五个建议:
(1) 将名称
bits
更改为更难看的名称,例如union_bits
,以清楚地表明某些不寻常的东西。(2) 回到OP拒绝的丑陋的转换,但将其丑陋隐藏在宏或内联函数中,如下所示:
但这会打破严格的别名规则。 (例如,参见 AndreyT 的回答:C99 严格别名规则C++ (GCC)。)
(3) 保留 Pixel 的原始定义,但进行更好的转换:
(4) 但这甚至更丑。您可以通过
typedef
修复此问题:使用 VC9 或使用 -pedantic 的 gcc,您需要(不要将其与 gcc 一起使用--参见末尾的注释):(
5)宏可能是首选。在 gcc 中,您可以非常巧妙地定义对任何给定类型的联合强制转换:
对于 VC9 和其他编译器,没有
typeof
,并且可能需要指针(不< /strong> 与 gcc 一起使用 - 请参阅末尾的注释):自记录,更安全。而且也不算太难看。所有这些建议都可能编译为相同的代码,因此效率不是问题。另请参阅我的相关答案:如何格式化函数指针?。
关于 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。(Edited) Both gcc and MSVC allow 'anonymous' structs/unions, which might solve your problem. For example:
gives (on Intel):
This requires changing all your declarations of struct Pixel to union Pixel in your original code. But this defect can be fixed via:
This also works with VC9, with 'warning C4201: nonstandard extension used : nameless struct/union'. Microsoft uses this trick, for example, in:
but they 'cheat' by suppressing the unwanted warning.
While the above examples are ok, if you use this technique too often, you'll quickly end up with unmaintainable code. Five suggestions to make things clearer:
(1) Change the name
bits
to something uglier likeunion_bits
, to clearly indicate something out-of-the-ordinary.(2) Go back to the ugly cast the OP rejected, but hide its ugliness in a macro or in an inline function, as in:
But this would break the strict aliasing rules. (See, for example, AndreyT's answer: C99 strict aliasing rules in C++ (GCC).)
(3) Keep the original definiton of Pixel, but do a better cast:
(4) But that is even uglier. You can fix this by a
typedef
:With VC9, or with gcc using -pedantic, you'll need (don't use this with gcc--see note at end):
(5) A macro may perhaps be preferred. In gcc, you can define a union cast to any given type very neatly:
With VC9 and other compilers, there is no
typeof
, and pointers may be needed (don't use this with gcc--see note at end):Self-documenting, and safer. And not too ugly. All these suggestions are likely to compile to identical code, so efficiency is not an issue. See also my related answer: How to format a function pointer?.
Warning about gcc: The GCC Manual version 4.3.4 (but not version 4.3.0) states that this last example, with
&(x)
, is undefined behaviour. See http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ and http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.联合内部结构的问题是,编译器可以在结构(或类)的成员之间添加填充字节,除了位字段。
给定:
这可以布局为:
因此结构的大小将为 16 字节。
当放入联合体时,编译器将采用两者中较大的容量来确定空间。此外,如您所见,32 位整数无法正确对齐。
我建议创建函数来组合并提取 32 位数量的像素。您也可以将其声明为内联:
这比联合内的结构(包括带有位字段的结构)可靠得多:
阅读此内容是否为红色仍然存在一些谜团是 MSB 或
alpha
是 MSB。通过使用位操作,阅读代码时不会有任何问题。只是我的建议,YMMV。
The problem with a structure inside a union, is that the compiler is allowed to add padding bytes between members of a structure (or class), except bit fields.
Given:
This could be laid out as:
So the size of the structure would be 16 bytes.
When put in a union, the compiler would take the larger capacity of the two to determine space. Also, as you can see, a 32 bit integer would not align correctly.
I suggest creating functions to combine and extract pixels from a 32-bit quantity. You can declare it
inline
too:This is a lot more reliable than a struct inside a union, including one with bit fields:
There is still some mystery when reading this whether
red
is the MSB oralpha
is the MSB. By using bit manipulation, there is no question when reading the code.Just my suggestions, YMMV.
为什么不把丑陋的混乱变成内联例程呢?类似于:
您还可以将此例程提供为
Pixel
的成员函数,称为i()
,这将允许您通过pixel.i 访问该值()
如果您愿意这样做。 (当不需要强制执行不变量时,我倾向于将功能与数据结构分开。)Why not make the ugly mess into an inline routine? Something like:
You could also provide this routine as a member function for
Pixel
, calledi()
, which would allow you to access the value viapixel.i()
if you preferred to do it that way. (I'd lean on separating the functionality from the data structure when invariants need not be enforced.)