是否通过C99中未指定的联合进行类型惩罚,并且它是否在C11中指定?

时间:2012-07-24 21:58:26

标签: c c99 unions c11 type-punning

Stack Overflow问题Getting the IEEE Single-precision bits for a float的一些答案建议使用union结构进行类型惩罚(例如:将float的位转换为uint32_t) :

union {
    float f;
    uint32_t u;
} un;
un.f = your_float;
uint32_t target = un.u;

但是,根据C99标准(至少草案n1124),联盟的uint32_t成员的值似乎未指定,其中第6.2.6.1.7节规定:

  

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

C11 n1570草案中至少有一个脚注似乎暗示不再是这种情况(见6.5.2.3中的脚注95):

  

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

但是,第C.6.6.1.7节中的案文与C11草案中的C99草案相同。

这种行为在C99下实际上是未指定的吗?它是否在C11中指定?我意识到大多数编译器似乎都支持这一点,但是知道它是在标准中指定还是只是一个非常常见的扩展名会很好。

4 个答案:

答案 0 :(得分:38)

带有union的类型punning的行为从C89更改为C99。 C99中的行为与C11相同。

正如Wug在他的回答中所指出的那样,C99 / C11中允许打字。当联合成员的大小不同时,将读取可能是陷阱的未指定值。

Clive D.W.在C99中添加了脚注。羽毛Defect Report #257

  

最后,从C90到C99的一个变化是当最后一个商店与另一个商店不同时,取消对访问联盟的一个成员的任何限制。理由是该行为将会取决于值的表示。由于这一点经常被误解,因此很可能在标准中明确说明。

     

[...]

     

要解决有关“打字”的问题,请在6.5.2.3#3中将新脚注78a附加到“指定成员”字样:   78a如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的相应部分将被重新解释为新类型中的对象表示形式如6.2.6所述(有时称为“打字”的过程)。这可能是陷阱表示。

Clive D.W.的措辞C委员会在Defect Report #283的回答中接受了羽毛技术勘误。

答案 1 :(得分:17)

原始的C99规范未指明此内容。

C99的技术勘误之一(TR2,我认为)增加了脚注82来纠正这种疏忽:

  

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

该脚注保留在C11标准中(它是C11中的脚注95)。

答案 2 :(得分:5)

这一直是" iffy"。正如其他人已经注意到,通过Technical Corregendum将一个脚注添加到C99。内容如下:

  

如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的相应部分将被重新解释为对象表示形式。 6.2.6中描述的新类型(有时称为"类型惩罚")。这可能是陷阱表示。

但是,脚注在前言中指定为非规范性的:

  

附件D和F构成本标准的规范部分;附件A,B,C,E,G,H,I,J,参考书目和索引仅供参考。根据ISO / IEC指令的第3部分,本前言,引言,注释,脚注和示例仅供参考

也就是说,脚注不能禁止行为;他们应该只澄清现有的文字。这是一个不受欢迎的观点,但上面引用的脚注实际上在这方面失败了 - 规范性文本中没有禁止这种行为。实际上,有一些部分如6.7.2.1:

  

...最多一个成员的值可以随时存储在一个联合对象中

结合6.5.2.3(关于使用"。"运营商访问工会成员):

  

该值是指定成员的值

即。如果只能存储一个成员的值,则另一个成员的值不存在。这强烈暗示通过联合的类型惩罚应该是可能的;成员访问产生一个不存在的值。 C11文件中仍然存在相同的文字。

然而,很明显,添加脚注的目的是允许打字;只是委员会似乎违反了不包含规范性文本的脚注规则。要接受脚注,你真的不得不忽视脚注不规范的部分,或者试图弄清楚如何以支持脚注结束的方式解释规范性文本(我试过,并且做错了。

您引用的部分:

  

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

但是,必须仔细阅读。 "对象表示的字节与该成员不对应"是指超出成员大小的字节,这本身并不是类型惩罚的问题(除非您不能假设写入工会成员将留下"额外的"任何较大成员的一部分不变)。

答案 3 :(得分:-1)

  

然而,这似乎违反了C99标准(至少草案n1124),其中第6.2.6.1.7节说明了一些内容。这种行为在C99下实际上是未指定的吗?

不,你没事。

  

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

这适用于不同大小的数据块。即,如果你有:

union u
{
    float f;
    double d;
};

你给f指定了一些东西,它会改变d的低4字节,但是高4字节将处于不确定状态。

工会主要用于打字。