我有以下程序
void swap(float * x, float * y)
{
float aux
aux = *x;
*x = *y;
*y = aux;
}
int main(void)
{
double a = 3.5, b = 5.6;
swap(&a, &b);
printf("%g %gn", a, b);
return 0;
}
程序编译,它显然抛出一些警告,但它运行,并且a, b
的值不会被交换。我不明白发生了什么,我会认为这要么不会编译,要么它会起作用,但这是非常不同的。
发生了什么事情?
程序调用未定义的行为,因此任何行为都是可能的。编译器不需要检测和拒绝这种情况,尽管优秀的编译器通常会警告这样的可疑代码。
它不会崩溃的可能原因是为 swap
函数生成的代码并不真正关心变量中的值,因为它没有对它们执行任何算术。由于它只是从一个变量复制到另一个变量,它只是将内存复制为字节 - 它与以下变量没有太大区别:
memcpy(&aux, x, sizeof(float));
memcpy(x, y, sizeof(float));
memcpy(y, &auz, sizeof(float));
这样做是交换 32 如果两个双打的 64 位。您没有看到任何变化的原因是这些是值的低阶位,它们被四舍五入。
此演示显示了交换前后两个十六进制值的内部表示形式(使用更多的类型双关语和 printf
)。
http://ideone.com/boN3Df
扩展Barmar的答案:你遇到的未定义行为是由于严格的混叠。严格锯齿是要求指向类型 A 的指针不能强制转换为类型 B 的指针,除非 B 是字节大小(或结构字段的一些魔术,请参阅什么是严格锯齿规则?)。我很想知道如果您使用 -fno-strict-aliasing
编译,它是否会交换元素.
编辑:除此之外,当您传递违反严格别名规则的函数指针时,编译器可以对该函数调用执行任何它想做的事情。在这种情况下,它可能只是忽略了它,因为在这种情况下它可以做的最快的事情是什么都没有。您可以检查的另一件事是,在启用和未启用优化的情况下,行为是否不同。
如果您尝试使用带有较长小数部分的双精度,您可以看到差异。
void swap(float * x, float * y)
{
float aux = *x;
*x = *y;
*y = aux;
}
int main(void)
{
double a = 3.0, b = 5.987654321;
printf("%f %fn", a, b);
swap(&a, &b);
printf("%f %fn", a, b);
return 0;
}
输出为:
3.000000 5.987654
3.000001 5.987652
标准双精度为 8 个字节,浮点数为 4 个字节。使用编译器,交换有效地交换每个浮点数的 4 位(它仍然是一个未定义的行为)。