传递错误类型参数时函数的奇怪行为

时间:2016-02-04 01:33:51

标签: c pointers

我有以下程序

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 %g\n", a, b);
  return 0;
}

程序编译,它显然会抛出一些警告,但它会运行,并且a, b的值不会被交换。我不明白发生了什么,我会认为这不会编译,或者它会起作用,但这是完全不同的。

发生了什么事?

3 个答案:

答案 0 :(得分:2)

您的程序调用未定义的行为,因此任何行为都是可能的。编译器并不需要检测和拒绝,尽管好的编译器经常会警告这样的可疑代码。

它不会崩溃的可能原因是为swap函数生成的代码并不真正关心变量中的值,因为它''没有对它们进行任何算术运算。由于它只是从一个变量复制到另一个变量,它只是将内存复制为字节 - 它与以下内容没有太大区别:

memcpy(&aux, x, sizeof(float));
memcpy(x, y, sizeof(float));
memcpy(y, &auz, sizeof(float));

如果两个中的64位加倍,那么这样做是交换32。你没有看到任何变化的原因是这些是价值的低位,它们会被抛弃。

此演示显示了交换前后十六进制中两个值的内部表示形式(使用printf更多类型惩罚)。

http://ideone.com/boN3Df

答案 1 :(得分:1)

要扩展Barmar的答案:您遇到的未定义行为是由于严格的别名造成的。严格别名是要求指向类型A的指针可能不会强制转换为类型B的指针,除非B是字节大小的(或者带有struct字段的魔术,请参阅What is the strict aliasing rule?)。如果您使用-fno-strict-aliasing进行编译,我很想知道它是否会交换元素。

编辑:要添加,当您传递违反严格别名规则的函数指针时,允许编译器对该函数调用执行任何操作。在这种情况下,它可能只是省略了它,因为在这种情况下它能做的最快的事情就是没有。您可以检查的另一件事是,在启用和不启用优化的情况下,行为是否不同。

答案 2 :(得分:0)

如果您尝试使用带有较长小数部分的double,您可以看到差异。

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 %f\n", a, b);
  swap(&a, &b);
  printf("%f %f\n", a, b);
  return 0;
}

输出结果为:

3.000000 5.987654
3.000001 5.987652

标准双精度数为8个字节,浮点数为4个字节。 使用您的编译器,交换有效地交换每个浮点数的4位(它仍然是一个未定义的行为)。