在C++中提及工会成员有多安全?

How safe is it to have references to union members in C++?

本文关键字:成员 安全 C++      更新时间:2023-10-16

>想象一下这种情况:

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,因为我不会通过阅读它们获得任何我不希望获得的价值,而不是如果我有一个浮点数和一个 int 的联合并引用了它们中的每一个。

提前谢谢。

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

但是,对于未unsigned char的实现定义类型,uint8_t可以是typedef(尽管仔细阅读标准表明,如果uint8_t存在,则它的大小必须与unsigned char相同)。 此外,这个问题仍然可以对其他类型的人感兴趣(例如uint32_tuint16_t)。

现有的引用绝对没有害处 - 如果在联合包含word而不是top+bottom时使用引用,则可能会出现唯一的危险。 另一方面,如果发生这种情况(并且top/bottomunsigned char),则违反了严格的混叠规则。

鉴于 C 标准明确允许通过联合进行类型双关(与 C++ 不同),您的 C++ 编译器也很有可能允许这样做。

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

† 在后来的版本中,措辞略有变化,章节编号可能有所变化——但原则保持不变。

假设你的代码实际上是:

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;

您可以像直接使用结构成员一样使用引用。

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

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


(*) 根据严格的别名规则,您不应访问与用于写入该值的类型不同的类型的值。由于struct bytesuint_8不同,混叠不正确。话虽如此,普通编译器目前接受它作为联合的扩展,甚至可以选择忽略严格的混叠规则。只是代码不符合标准,另一个编译器的行为可能不同。