i == (i = 2) 的结果是什么?

以下代码如下:

// 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()));
    }
}

那么,如何解释以上两种不同的执行模式呢?


答案 1

执行表达式的 C 程序的行为未定义的i == (i = 2)

它来自C11 6.5p22

  1. 如果相对于同一标量对象上的不同副作用或使用同一标量对象的值计算,标量对象上的副作用未排序,则行为未定义。如果表达式的子表达式有多个允许的排序,则当任何排序中发生这种无序列的副作用时,行为是未定义的。

左侧是对标量对象的值进行值计算,右侧具有将值赋给 的副作用。的 LHS 和 RHS 是无序列的 w.r.t. 。因此,整个程序在C中是没有意义的。i==ii = 22i==

编译与,GCC将吐出:gcc -Wall

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;

答案 2

Java 语言规范 (§15.7) 规定:

Java编程语言保证运算符的操作数看起来是按特定的评估顺序(即从左到右)计算的。

规范 (§15.21.1) 还规定:

运算符生成的值是左操作数的值等于右操作数的值;否则,结果为 。==truefalse

因此,在Java中,运行时的if语句将如下所示,其计算结果显然为:false

if (1 == 2) {

}

在C中,它只是未定义的(参见Antti的答案)。