用联合检测endianess是否安全?

换句话说,根据C 标准 ,这段代码是否安全? (假设uint8_t是一个字节)

 void detectEndianness(void){ union { uint16_t w; uint8_t b; } a; aw = 0x00FFU; if (ab == 0xFFU) { puts("Little endian."); } else if (ab == 0U) { puts("Big endian."); } else { puts("Stack Overflow endian."); } } 

如果我把它改成这个怎么办? 注意第三个我知道的情况。

 aw = 1U; if (ab == 1U) { puts("Little endian."); } else if (ab == 0U) { puts ("Big endian."); } else if (ab == 0x80U) { /* Special potential */ } else { puts("Stack Overflow endian."); } 

引自n1570:

6.5.2.3结构和工会成员 – 第3页

后缀表达式后跟。 运算符和标识符指定结构或联合对象的成员。 该值是指定成员的值,如果第一个表达式是左值,则该值是左值。

6.2.6类型的表示/ 1概述 – 第7页

当值存储在union类型的对象的成员中时,对象表示的不对应于该成员但对应于其他成员的字节采用未指定的值。

这是允许的。 如果考虑到注释95 (尽管只提供信息),您的用例甚至可以被视为一个预期目的:

如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的相应部分重新解释为新类型中的对象表示forms在6.2.6中描述(一个过程有时称为“类型双关语”)。 这可能是陷阱表示。

现在,因为uintN_t系列类型被定义为没有填充位

7.20.1.1精确宽度整数类型 – p2

typedef名称uintN_t指定一个宽度为N且没有填充位的无符号整数类型。 因此,uint24_t表示这种无符号整数类型,其宽度恰好为24位。

它们的所有位表示都是有效值,没有陷阱表示是可能的。 所以我们必须得出结论,它确实会检查uint16_t结尾。

该标准(在链接的在线草案中提供)在脚注中说,允许访问与先前编写的成员相同的联合的不同成员:

95)如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为新对象表示如6.2.6所述的类型(有时称为”punning”的过程)。 这可能是陷阱表示。

但脚注还提到了一个可能的陷阱表示,并且标准保证的唯一数据类型是关于陷阱表示的安全性是unsigned char 。 访问陷阱表示可能是未定义的行为; 虽然我不认为unit_32可能会在您的平台上产生陷阱表示,但实际上是依赖于实现是否访问该成员是否为UB。

不要求字节内的位顺序与较大类型中相应位的排序相匹配。 例如,定义uint32_t并具有8位unsigned char的一致实现可以使用每个字节的四个位存储uint32_t的高16位,并使用每个字节的剩余四位存储低16位。 从标准的角度来看,32个中的任何一个! 比特的排列同样可以接受。

话虽如此,任何不是故意钝的设计并且设计为在普通平台上运行的实现都将使用两个排序中的一个[将字节视为8个连续位的组,顺序为0123或3210],以及不使用上述任何一个和目标任何不完全模糊的平台将使用2301或1032.标准不禁止其他排序,但不适应它们将不会导致任何麻烦,除非使用钝 – 实施的实施。