这个指针构建是否会破坏严格的别名规则?

时间:2013-04-04 18:29:11

标签: c pointers casting c99 strict-aliasing

这是Quake III Arena的快速反平方根实现:

float Q_rsqrt( float number )
{
        long i;
        float x2, y;
        const float threehalfs = 1.5F;

        x2 = number * 0.5F;
        y  = number;
        i  = * ( long * ) &y;                       // evil floating point bit level hacking
        i  = 0x5f3759df - ( i >> 1 );               // what?
        y  = * ( float * ) &i;
        y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//      y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

        return y;
}

我注意到 long int i获取 float {{1}的地址(强制转换为long *)的解除引用值}。然后代码在y上执行操作,然后将解除引用的值存储在i的地址(强制转换为float *)到i

这会破坏严格的别名规则,因为yi的类型不同吗?

我认为也许它没有,因为该值被解除引用并且被复制;所以操作是在副本而不是原件上进行的。

4 个答案:

答案 0 :(得分:6)

是的,此代码严重破坏并调用未定义的行为。特别要注意这两行:

    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking

由于对象*(long *)&y具有类型long,编译器可以自由地假设它不能为类型为float的对象设置别名;因此,编译器可以相对于彼此重新排序这两个操作。

要修复它,应该使用联合。

答案 1 :(得分:4)

是的,它打破了别名规则。

在现代C中,您可以将i = * (long *) &y;更改为:

i = (union { float f; long l; }) {y} .l;

y = * (float *) &i;

y = (union { long l; float f; }) {i} .f;

如果你保证在使用的C实现中longfloat具有合适的大小和表示,那么行为由C标准定义:一个对象的字节type将被重新解释为另一种类型。

答案 2 :(得分:3)

是的,它打破了别名规则。

i = * ( long * ) &y;这样的最干净的解决方案就是:

  memcpy(&i, &y, sizeof(i)); // assuming sizeof(i) == sizeof(y)

避免了对齐和别名问题。启用优化后,通常只需用少量指令替换对memcpy()的调用。

正如任何其他方法所建议的那样,此方法不会解决与陷阱表示相关的任何问题。但是,在大多数平台上,整数中没有陷阱表示,如果您知道浮点格式,则可以避免使用浮点格式陷阱表示(如果有)。

答案 3 :(得分:1)

i = * ( long * ) &y;

这会破坏别名规则,因此会调用未定义的行为。

您正在访问类型不同于y的对象float,或char的签名/无符号变体。

y = * ( float * ) &i;

上述声明也打破了别名规则。