在http://blog.regehr.org/archives/1307中,作者声称以下代码段具有未定义的行为:
unsigned long bogus_conversion(double d) {
unsigned long *lp = (unsigned long *)&d;
return *lp;
}
该参数基于http://port70.net/~nsz/c/c11/n1570.html#6.5p7,它指定了允许的访问环境。但是,在这个项目符号的脚注(88)中,它说这个列表仅用于检查别名,所以我认为这个片段很好,假设为sizeof(long) == sizeof(double)
。
我的问题是上述代码段是否被允许。
答案 0 :(得分:0)
该代码段是错误的,但不是因为别名。首先,有一个简单的规则,即使用与其有效类型不同的类型来指向对象的指针是错误的。这里的有效类型为double
,因此存在错误。
标准中存在此保护措施,因为double
的位表示不能是unsigned long
的有效表示,尽管现在这种情况非常奇特。
其次,从更实际的角度来看,double
和unsigned long
可能具有不同的对齐属性,以这种方式访问它可能会产生总线错误或只是运行时间损失。< / p>
一般来说,像这样的铸造指针几乎总是错误的,没有明确的行为,是不好的风格,而且无论如何都是无用的。在关于这些问题的论证中关注别名是一个坏习惯,可能源于难以理解和可怕的gcc警告。
如果你真的想知道某种类型的位表示,有一些例外的&#34;有效类型&#34;规则。有两种便携式解决方案由C标准明确定义:
unsigned char*
并检查字节。union
,将值存储在其中并使用其他类型读取。通过这个你告诉编译器你想要一个可以看作两种类型的对象。但是在这里你不应该使用unsigned long
作为目标类型,而是使用uint64_t
,因为你必须确保大小正是你所想的那样,并且没有陷阱表示。为了说明这一点,这里的功能与问题中的功能相同,但具有已定义的行为。
unsigned long valid_conversion(double d) {
union {
unsigned long ul;
double d;
} ub = { .d = d, };
return ub.ul;
}
我的编译器(在Debian上的gcc,没有什么花哨的)将这个编译成与问题中的代码完全相同的汇编程序。只有你知道这段代码是可移植的。