C,前后增量,不同程序中的不同答案

时间:2016-12-28 16:18:00

标签: c post-increment pre-increment

在学习C语言时测试一些代码,

#include <stdio.h>
#include <math.h>
#define hypotenusa(x, y)  sqrt((x) * (x) + (y) * (y))

int main(void) {
    int a, x;
    x = 2;
    a = hypotenusa(++x, ++x);
    printf("%d\n", a);
}

我得到了答案

  • 6在一个程序中(dosbox gcc编译器)
  • codelight gcc编译器中的
  • 7
  • 在codeChef在线编译器上
  • 8

任何人都可以解释这种行为吗? 我的逻辑说它应该是6sqrt(42))但是......

3 个答案:

答案 0 :(得分:6)

它是undefined behaviour

宏替换后

a = hypotenusa(++x, ++x);

变为:

a = sqrt((++x) * (++x) + (++x) * (++x));

正如您所看到的,x被多次修改而没有任何中间序列点。请参阅What Every C Programmer Should Know About Undefined Behavior

答案 1 :(得分:2)

hypotenusa(++x, ++x)是未定义的行为。
由编译器决定哪个参数首先递增(和推送) - 在宏扩展之后,总共有四个实例,并且未定义序列。

您不应该在同一语句中多次增加同一个变量,以避免此类问题。在宏中使用变量两次可以隐藏此错误并使其非常讨厌。

答案 2 :(得分:2)

您的宏的行为未定义,这意味着任何结果都是可能的。

Chapter and verse

6.5表达式
...
2如果相对于不同的副作用,对标量物体的副作用是无效的 在相同的标量对象上或使用相同标量的值进行值计算 对象,
行为未定义。如果有多个允许的排序 子表达式
如果这样一个未经测序的一方,那么这个行为是不确定的 效果发生在任何中 的排序。 84) 84)此段落呈现未定义的语句表达式,例如
    i = ++i + 1;
    a[i++] = i;
同时允许
    i = i + 1;
    a[i] = i;

基本上,如果在表达式中多次修改对象的值,或者同时修改对象并在表达式的计算中使用其值,则除非有这些操作之间的序列点。除少数例外情况外,C不强制对表达式或函数参数评估进行从左到右的评估,也不强制要求在评估后立即应用++--的副作用。因此,像x++ * x++这样的表达式的结果会因平台,程序到程序,甚至可能从运行到运行而有所不同(尽管我在实践中从未见过)。

例如,给定表达式y = x++ * x++,可以使用以下评估序列:

t0 <- x        // right hand x++, t0 == 2
t1 <- x        // left hand x++, t1 == 2
y <- t0 * t1   // y = 2 * 2 == 4
x <- x + 1     // x == 3
x <- x + 1     // x == 4

如果您假设从左到右评估,则不会给出您期望的结果。