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

这是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 ifloat y的地址(转换为long * )中取消引用的值。 然后代码在i上执行操作,然后将解除引用的值存储在i的地址(转换为float * )中。

这是否会破坏严格的别名规则,因为iy类型不同?

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

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

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

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

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

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

在现代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标准定义:一种类型的对象的字节将被重新解释为另一种类型。

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

对于像i = * ( long * ) &y;这样的东西最干净的修复i = * ( long * ) &y; 会是这样的:

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

它避免了对齐和锯齿问题。 在启用优化的情况下,对memcpy()的调用通常应该只用几条指令替换。

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

i = * ( long * ) &y;

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

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

y = * ( float * ) &i;

上面的这个陈述也打破了别名规则。