想象一下这种情况:
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
的任何地方都没有引用,只有top
和bottom
。特别是在这种特殊情况下,引用应该指向整个u16的顶部和底部两半,所以没有任何UB,因为我不会得到任何UB如果我有一个浮点数和一个int的并集并且引用了每个值,那么我不希望通过读取它们来获得它。
提前致谢。
答案 0 :(得分:0)
首先要说的是严格别名规则(C ++ 03标准第3.10节)†中有明确的语言,允许通过unsigned char
访问值。如果uint8_t
是unsigned char
的typedef,则此用法将是明确合法的。
但是,对于不是uint8_t
的实现定义类型,unsigned char
可以是typedef(尽管仔细阅读标准表明如果存在uint8_t
,它必须与unsigned char
)。此外,对于其他类型(例如uint32_t
和uint16_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 bytes
与uint_8
不同,因此别名不正确。话虽这么说,常见的编译器目前接受作为联盟的扩展,甚至可以选择忽略严格的别名规则。只是代码不符合标准,而另一个编译器可能表现不同。