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

kan*_*wei 44 c java side-effects variable-assignment language-lawyer

运行以下代码:

// In Java, output #####
public static void main(String[] args) {
    int i = 1;

    if(i == (i = 2)) {
        System.out.println("@@@@@");
    } else {
        System.out.println("#####");
    }
}
Run Code Online (Sandbox Code Playgroud)

但:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

提出这个问题的动机来自以下代码:

// 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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Ant*_*ala 60

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

它来自C11 6.5p22:

  1. 如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.84)

i上的左手侧==是在标量对象的值的值计算i和所述的右手侧i = 2具有分配值的副作用2i.LHS和RHS ==相互之间没有相应的序列.因此整个程序在C中毫无意义.

编译gcc -Wall和GCC将吐出:

unsequenced.c:5:16: warning: operation on ‘i’ may be undefined [-Wsequence-point]
     if(i == (i = 2)) {
             ~~~^~~~
Run Code Online (Sandbox Code Playgroud)

与C不同,Java保证了操作数的评估顺序(从左到右)

haveNext = (prev == (prev = get()));
Run Code Online (Sandbox Code Playgroud)

在Java中是正确的.在评估RHS的副作用发生之前,严格确定LHS的值.

在C中你必须写这样的东西

newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;
Run Code Online (Sandbox Code Playgroud)


Jac*_* G. 16

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

Java编程语言保证运算符的操作数似乎以特定的评估顺序进行评估,即从左到右.

规范(§15.21.1)也指出:

==运算符产生true的值是左手操作数的值是否等于右手操作数的值; 否则,结果是 false.

因此在Java中,运行时的if语句看起来如下所示,显然评估为false:

if (1 == 2) {

}
Run Code Online (Sandbox Code Playgroud)

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


Joh*_*ode 5

在C中,行为i == (i = 2)是未定义的,因为它尝试更新对象并在计算中使用该对象的值而没有插入序列点.结果将根据编译器,编译器设置,甚至周围的代码而有所不同.