sfs*_*man 58 c c99 unions type-punning c11
Stack Overflow问题的一些答案获取浮点数的IEEE单精度位建议使用union类型双关的结构(例如:将a的位float转换为a uint32_t):
union {
float f;
uint32_t u;
} un;
un.f = your_float;
uint32_t target = un.u;
Run Code Online (Sandbox Code Playgroud)
但是,uint32_t根据C99标准(至少草案n1124),联盟成员的值似乎未指定,其中第6.2.6.1.7节规定:
当值存储在union类型的对象的成员中时,对象表示的字节与该成员不对应但与其他成员对应的字节采用未指定的值.
C11 n1570草案至少有一个脚注似乎暗示不再是这种情况(见6.5.2.3中的脚注95):
如果用于读取union对象的内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的适当部分重新解释为新类型中的对象表示形式在6.2.6中描述(一个过程有时被称为''punning'').这可能是陷阱表示.
但是,第C.6.6.1.7节中的案文与C11草案中的C99草案相同.
这种行为在C99下实际上是未指定的吗?它是否在C11中指定?我意识到大多数编译器似乎都支持这一点,但是知道它是在标准中指定还是只是一个非常常见的扩展会很好.
oua*_*uah 38
带有union的类型双关的行为从C89变为C99.C99中的行为与C11相同.
正如Wug在他的回答中指出的那样,在C99/C11中允许打字.当联合成员的大小不同时,将读取可能是陷阱的未指定值.
在克莱夫DW羽毛缺陷报告#257之后,在C99中添加了脚注:
最后,从C90到C99的一个变化是当最后一个商店到另一个商店时,取消了对访问一个工会成员的任何限制.理由是,行为将取决于价值的表示.由于这一点经常被误解,因此很可能值得在标准中明确说明.
[...]
要解决有关"打字"的问题,请在6.5.2.3#3中添加新的脚注78a到"命名成员"一词:78a如果用于访问联合对象内容的成员与上一个成员不同用于在对象中存储值,值的对象表示的适当部分被重新解释为新类型中的对象表示,如6.2.6中所述(有时称为"类型双关"的过程).这可能是陷阱表示.
在C #缺陷报告#283的答案中,Clive DW Feather的措辞被接受为技术勘误.
Ste*_*non 17
最初的C99规范没有具体说明.
C99(TR2,我认为)的技术勘误之一增加了脚注82来纠正这种疏忽:
如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则将值的对象表示的相应部分重新解释为新类型中的对象表示形式在6.2.6中描述(一个过程有时称为"类型双关语").这可能是陷阱表示.
该脚注保留在C11标准中(它是C11中的脚注95).
这一直是“无聊的”。正如其他人指出的那样,已通过技术勘误表将脚注添加到C99。内容如下:
如果用于访问联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示形式的适当部分将重新解释为新类型的对象表示形式,如下所示:在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文档中仍然存在相同的文本。
但是,很明显,添加脚注的目的是为了进行类型处理。只是委员会似乎违反了不包含规范性文字的脚注中的规则。要接受脚注,您实际上必须无视说明脚注不是规范性的部分,或者尝试弄清楚如何以支持脚注结论的方式解释规范性文本(我已经尝试过,并且失败,去做)。
您引用的部分:
当值存储在联合类型的对象的成员中时,与该成员不对应但与其他成员对应的对象表示形式的字节采用未指定的值。
但是,必须仔细阅读。“ 与该成员不对应的对象表示形式的字节”指的是超出该成员大小的字节,这本身不是punning类型的问题(除非您不能假定写工会成员将导致未触及的任何较大成员的“额外”部分)。