在C ++中引用union成员有多安全?

时间:2017-03-29 08:49:21

标签: c++

想象一下这种情况:

union Reg16
{
    uint16_t word;

    struct
    {
        uint8_t bottom;
        uint8_t top;
    };
};

让我们说我在某个地方使用这个联盟:

Reg16 reg_AF;
uint8_t& reg_A = reg_AF.top;
uint8_t& reg_F = reg_AF.bottom;

即使工会在技术上并不总是指向u8,是否可以安全地引用工会的相应部分?我不确定这是否违反严格的别名规则,但值得注意的是,word的任何地方都没有引用,只有topbottom。特别是在这种特殊情况下,引用应该指向整个u16的顶部和底部两半,所以没有任何UB,因为我不会得到任何UB如果我有一个浮点数和一个int的并集并且引用了每个值,那么我不希望通过读取它们来获得它。

提前致谢。

2 个答案:

答案 0 :(得分:0)

首先要说的是严格别名规则(C ++ 03标准第3.10节)†中有明确的语言,允许通过unsigned char访问值。如果uint8_tunsigned char的typedef,则此用法将是明确合法的。

但是,对于不是uint8_t的实现定义类型,unsigned char可以是typedef(尽管仔细阅读标准表明如果存在uint8_t,它必须与unsigned char)。此外,对于其他类型(例如uint32_tuint16_t),此问题仍然有用。

现有引用肯定没有任何损害 - 当联合包含word而不是top + {{1}时,如果引用 ,则可能出现唯一的危险}}。另一方面,如果发生这种情况(且bottom / top不是bottom),则违反了严格别名规则。

鉴于C标准明确允许通过联合进行类型惩罚(与C ++不同),您的C ++编译器也很可能允许它。

除此之外:严格来说,样本不合法;匿名结构是GCC,Clang和MSVC支持的扩展(所以问题是,哪些编译器支持它们)。

†在以后的版本中,措辞略有改变,部分编号可能已经改变 - 但原则保持不变。

答案 1 :(得分:0)

假设你的代码实际上是:

union Reg16
{
    uint16_t word;

    struct
    {
        uint8_t bottom;
        uint8_t top;
    } bytes;
};

这是完美的作为声明

Reg16 reg_AF;
uint8_t& reg_A = reg_AF.bytes.top;
uint8_t& reg_F = reg_AF.bytes.bottom;

您可以完全按照直接使用struct成员的方式使用引用。

例如reg_AF.bytes.bottom = 'A'; std::cout << ref_F;非常好:联合当前包含结构,并且您正确使用对结构成员的引用。

但是这个reg_AF.word = 0x4142; std::cout << reg_A;与UB(*)完全一样,是reg_AF.word = 0x4142; std::cout << reg_AF.bytes.bottom;

(*)根据严格别名规则,您不应该从用于编写它的其他类型访问值。由于struct bytesuint_8不同,因此别名不正确。话虽这么说,常见的编译器目前接受作为联盟的扩展,甚至可以选择忽略严格的别名规则。只是代码不符合标准,而另一个编译器可能表现不同。