我使用联合表示rgb像素数据,因此可以将其作为uint8_t
的连续数组或作为单个rgb元素进行访问。 (我认为这可能是工会的少数用途之一。)
类似这样的东西:
union PixelRGB
{
uint8_t array[3];
struct rgb
{
uint8_t b;
uint8_t g;
uint8_t r;
};
};
我实际上是从其他地方的网上捡来的代码,建议将此作为潜在的用途。
几分钟前,我问了一个有关另一个问题的问题,但被告知我的工会可能出示了UB。
我已经阅读了this,并且似乎表明如果我从不是最后写入的成员中读取某个成员,则结果为UB ...
我的问题是为什么?
我的假设(也许是错误的?)是在内存中,联合将表示为:
b = array[0]
g = array[1]
r = array[2]
ie;这些变量中的每一个在内存中都占用完全相同的字节(空间/位置/地址),因此,我假设这行
g = 0xff;
将array[1]
的值更改为0xff
。
我错了吗?为什么?
出于参数考虑:C ++ 14
重复的问题不是重复的:链接的问题询问一个联合中的两个结构是否为UB-不完全是我在这里问的。
答案 0 :(得分:4)
我已经读完了它,它的确表明如果我从不是要写入的最后一个成员的成员中读取,则结果为UB ...
我的问题是为什么?
因为该标准是这样的 1 。该标准有时会向我们(C ++开发人员)强加一些规则,这些规则允许实现(编译器)忽略边缘情况,并允许它们针对名义情况进行优化。这是一条规则。
某些编译器可能会按预期方式生成二进制文件。有些可能会产生崩溃的可执行文件。有些可能会在两者之间做任何事情,或者似乎是随机的。 未定义的行为是未定义的 2 。
1)
[class.union]/1
在联合中,如果非静态数据成员的名称引用其生存期已开始且尚未结束([basic.life])的对象,则该成员为活动状态。联合类型对象的最多一个非静态数据成员可以随时处于活动状态,也就是说,最多可以随时将一个非静态数据成员的值存储在联合中。 / p>
和
[basic.life]/7
类似地,在对象的生存期开始之前但已分配了该对象将占用的存储空间之后,或者在对象的生存期结束之后以及重新使用或释放该对象占用的存储空间之前,所有glvalue可以使用指代原始对象的符号,但只能以有限的方式使用。 [...] 如果该程序具有未定义的行为,:
- glvalue用于访问对象,或
- [...]
在以下情况下,更易于为人类解析:
union { unsigned a; char b[sizeof(unsigned)]; } u;
u.a = 0; // (1)
(void) u.b[0]; // (2) UB
在标记为(1)
的行上,u.a
现在是u
的活动成员。对于[class.union]/1
,由于工会中只有一个非静态成员可以同时处于活动状态,因此u.b
是 not 处于活动状态。
这意味着在标记为(2)
的行上,我们访问一个对象的值,该对象的生存期尚未开始或已经结束,从而使其按照[basic.life]/7
的行为未定义。
2)
[defns.undefined]
未定义的行为
本文档对此没有要求的行为
答案 1 :(得分:2)
这是未定义的行为,在标准中明确如此。您不能用一个写一个联合并读另一个(期望适用)并在C ++中获得定义的行为。
但是,它也可以在您可能会遇到的任何编译器上运行,并且如果您正在使用RGB值和像素进行处理,则您的代码仍然可能与平台相关联,因此我的建议不必担心。< / p>
答案 2 :(得分:0)
显然,该假设可能取决于编译器。因此,我发现这是一种替代解决方案:
class PixelRGB
{
public:
unsigned char array[3];
unsigned char &r;
unsigned char &g;
unsigned char &b;
PixelRGB()
: r{array[2]}
, g{array[1]}
, b{array[0]}
{
}
}
您可能必须注意自己的字节序。如此明显的解决方案。它本质上是一个软件联合。缺点是此结构使用大量内存(除非编译器可以以某种方式优化引用?)-但这不是问题。将数据存储为unsigned char[]
,然后仅将其用于操作,这正是我的预期目的。