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

Vil*_*ray 9 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;
}
Run Code Online (Sandbox Code Playgroud)

我注意到long int ifloat的地址(转换为a long *)处取消引用的值.然后代码在将解除引用的值存储到into 的地址(强制转换为a )之前执行操作. yifloat *iy

这会打破严格的别名规则,因为i它的类型不一样y吗?

我想也许它不会,因为价值被解除引用和复制 ; 因此操作是在副本而不是原件上执行的.

R..*_*R.. 6

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

    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
Run Code Online (Sandbox Code Playgroud)

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

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


Eri*_*hil 5

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

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

i = (union { float f; long l; }) {y} .l;
Run Code Online (Sandbox Code Playgroud)

y = * (float *) &i;

y = (union { long l; float f; }) {i} .f;
Run Code Online (Sandbox Code Playgroud)

只要你有,在C实现所使用的保证,long并且float具有合适的尺寸和表示,则该行为由C标准定义:一种类型的对象的将被重新解释为另一种类型的字节数。