在学习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编译器)7
和8
任何人都可以解释这种行为吗?
我的逻辑说它应该是6
(sqrt(42)
)但是......
答案 0 :(得分:6)
宏替换后
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)
您的宏的行为未定义,这意味着任何结果都是可能的。
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
如果您假设从左到右评估,则不会给出您期望的结果。