运行以下代码:
// In Java, output #####
public static void main(String[] args) {
int i = 1;
if(i == (i = 2)) {
System.out.println("@@@@@");
} else {
System.out.println("#####");
}
}
但是:
// In C, output @@@@@,I did test on Clion(GCC 7.3) and Visual Studio 2017
int main(int argc, char *argv[]) {
int i = 1;
if(i == (i = 2)) {
printf("@@@@@");
} else {
printf("#####");
}
return 0;
}
问这个问题的动机来自以下代码:
// The code is from the JDK 11 - java.util.concurrent.atomic.AtomicInteger
// I am curious about the behavior of the variable prev.
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.applyAsInt(prev);
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
那么,如何解释以上两种不同的执行方式?
答案 0 :(得分:60)
执行表达式i == (i = 2)
的C程序的行为是未定义。
它来自C11 6.5p22:
- 如果相对于相同标量对象上的不同副作用或使用相同标量对象的值进行的值计算,相对于标量对象的副作用未排序,则行为未定义。如果一个表达式的子表达式有多个允许的排序,则如果在任何排序中都出现这种无序的副作用,则行为是不确定的。84)
i
左侧的==
是对标量对象i
的值进行的值计算,右侧i = 2
的一侧将值2
分配给i
的效果。 ==
的LHS和RHS没有序列号。彼此。因此,整个程序在C语言中毫无意义。
使用gcc -Wall
进行编译,GCC会吐出:
unsequenced.c:5:16: warning: operation on ‘i’ may be undefined [-Wsequence-point]
if(i == (i = 2)) {
~~~^~~~
与C不同,Java保证操作数(从左到右)的求值顺序,因此
haveNext = (prev == (prev = get()));
在Java中是正确的。 LHS的值是在评估RHS的副作用之前严格确定的。
在C语言中,您必须将其写为类似
newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;
答案 1 :(得分:16)
Java Language Specification (§15.7)指出:
Java编程语言保证运算符的操作数出现 可以按照特定的评估顺序进行评估,即从左到右。
如果左侧的值是
==
运算符,则生成的值为true
操作数等于右侧操作数的值;否则,结果是false
。
因此,在Java中,运行时的if语句如下所示,其显然为false
:
if (1 == 2) {
}
在C语言中,它只是未定义的(请参见Antti's answer)。
答案 2 :(得分:5)
在C语言中,i == (i = 2)
的行为是不确定的,因为它试图更新对象并在没有中间序列点的情况下在计算中使用该对象的值。结果将根据编译器,编译器设置甚至周围的代码而有所不同。