C ++,我认为我的工会可能正在产生不确定的行为

时间:2018-12-26 10:51:19

标签: c++ undefined-behavior unions

我使用联合表示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-不完全是我在这里问的。

3 个答案:

答案 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[],然后仅将其用于操作,这正是我的预期目的。