用联合检测结束是否安全?

时间:2017-11-22 09:07:07

标签: c language-lawyer endianness unions

换句话说,根据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."); }

3 个答案:

答案 0 :(得分:6)

从n1570引用:

6.5.2.3 Structure and union members - p3

  

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

6.2.6 Representations of types / 1 General - p7

  

当一个值存储在union类型的对象的成员中时,   对象表示的字节与该对应的字节不对应   成员,但确实对应其他成员采取未指定的值。

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

  

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

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

7.20.1.1 Exact-width integer types - p2

  

typedef名称uintN_t指定带有的无符号整数类型   宽度N和无填充位。因此,uint24_t表示这样的无符号   整数类型,宽度恰好为24位。

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

答案 1 :(得分:1)

标准(在链接的在线草案中提供)在Unity stsatistics中表示允许访问与先前写入的成员相同的联合的不同成员:

  

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

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

答案 2 :(得分:0)

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

话虽如此,任何非故意使用并且设计为在普通平台上运行的实现都将使用两种顺序之一[将字节视为8个连续位的组,顺序为0123或3210],并且不使用上述任何一个并且任何不完全模糊的平台将使用2301或1032.标准不禁止其他排序,但不适应它们将不太可能造成任何麻烦,除了当使用迟钝的实施时。