c cast and deference a pointer 严格别名
c cast and deference a pointer strict aliasing
在 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)
.
我的问题是上面的片段是否被允许。
代码段有误,但不是因为别名。首先,有一个简单的规则表明,将指针引用到具有与其有效类型不同的类型的对象是错误的。这里的有效类型是double
,所以有错误。
这种保护措施存在于标准中,因为 double
的位表示不能是 unsigned long
的有效表示,尽管现在这很奇特。
其次,从更实际的角度来看,double
和 unsigned long
可能具有不同的对齐属性,以这种方式访问它可能会产生总线错误或只有 运行 时间惩罚。
一般来说,像这样的指针几乎总是错误的,没有定义的行为,是糟糕的风格,而且无论如何基本上都是无用的。在关于这些问题的争论中专注于 aliasing 是一个坏习惯,可能源于难以理解和可怕的 gcc 警告。
如果你真的想知道某种类型的位表示,"effective type"规则有一些例外。 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,没什么特别的)将它编译成 完全 与问题中的代码相同的汇编程序。只有你知道这个代码是可移植的。
在 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)
.
我的问题是上面的片段是否被允许。
代码段有误,但不是因为别名。首先,有一个简单的规则表明,将指针引用到具有与其有效类型不同的类型的对象是错误的。这里的有效类型是double
,所以有错误。
这种保护措施存在于标准中,因为 double
的位表示不能是 unsigned long
的有效表示,尽管现在这很奇特。
其次,从更实际的角度来看,double
和 unsigned long
可能具有不同的对齐属性,以这种方式访问它可能会产生总线错误或只有 运行 时间惩罚。
一般来说,像这样的指针几乎总是错误的,没有定义的行为,是糟糕的风格,而且无论如何基本上都是无用的。在关于这些问题的争论中专注于 aliasing 是一个坏习惯,可能源于难以理解和可怕的 gcc 警告。
如果你真的想知道某种类型的位表示,"effective type"规则有一些例外。 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,没什么特别的)将它编译成 完全 与问题中的代码相同的汇编程序。只有你知道这个代码是可移植的。