换句话说,根据c 标准,此代码安全吗?(假设uint8_t
是一个字节)
void detectEndianness(void){
union {
uint16_t w;
uint8_t b;
} a;
a.w = 0x00FFU;
if (a.b == 0xFFU) {
puts("Little endian.");
}
else if (a.b == 0U) {
puts("Big endian.");
}
else {
puts("Stack Overflow endian.");
}
}
如果我将其更改为此怎么办?注意我知道的第三个if
情况。
a.w = 1U;
if (a.b == 1U) { puts("Little endian."); }
else if (a.b == 0U) { puts ("Big endian."); }
else if (a.b == 0x80U) { /* Special potential */ }
else { puts("Stack Overflow endian."); }
引用N1570:
6.5.2.3结构和工会成员-P3
后缀表达式,然后是。操作员和标识符 指定结构或联合对象的成员。值是 命名成员的,如果第一个表达式为一个 lvalue。
6.2.6类型的表示/1常规-P7
当一个值存储在联合类型的对象的成员中时 对象表示的字节与该字节不符 成员,但确实与其他成员相对应为未指定的值。
允许。如果考虑到注释95,您的用例甚至可以被视为一个预期的目的(尽管仅提供了信息):
如果成员用来读取联合对象的内容不是 与上次用于在对象中存储值的成员相同, 该值的对象表示的适当部分是 如所述,将新类型的对象表示重新解释为 在6.2.6中(有时称为"类型punning"的过程)。这可能是 陷阱表示。
现在,由于UINTN_T类型家族的定义为没有填充位
7.20.1.1精确宽度整数类型-P2
typedef名称uintn_t指定一个未签名的整数类型 宽度n,没有填充位。因此,uint24_t表示这样的未签名 整数类型,宽度正好为24位。
他们所有的位表示都是有效的值,不可能陷阱表示。因此,我们必须得出结论,它确实会检查uint16_t
的Endianess。
标准(在链接的在线草稿中可用)在脚注中说,它可以访问与先前编写的成员的其他工会的其他成员:
95)如果成员用来读取联合对象的内容不是 与上次用于在对象中存储值的成员相同, 该值的对象表示的适当部分是 如所述,将新类型的对象表示重新解释为 在6.2.6中(有时称为"类型punning"的过程)。这可能是 陷阱表示。
,但脚注还提到了可能的陷阱表示,并且标准保证的唯一数据类型是安全的陷阱表示形式是unsigned char
。访问陷阱表示可能是未定义的行为;尽管我认为unit_32
不会在您的平台上产生陷阱表示,但实际上取决于访问该成员是否为UB。
不需要字节内的位顺序匹配较大类型中相应位的顺序。定义uint32_t
并具有8位unsigned char
的符合实现,例如,可以使用每个字节中的四个位存储UINT32_T的上部16位,并使用每个字节的其余四个位存储底部16位。从标准的角度来看,32中的任何一个!位的排列将同样可以接受。
据说,任何没有故意钝的实现且旨在在普通平台上运行的任何实现都将使用两个订单之一(将字节视为连续8位的组,按0123或3210的顺序进行),一个不使用上述并针对任何不完全晦涩的平台的一种将使用2301或1032。该标准不禁止其他订单,但是不容纳它们,除非不可容纳它们,否则可能会造成任何麻烦使用狂热的实现时。