【问题标题】:What is the result of i == (i = 2)?i == (i = 2) 的结果是什么?
【发布时间】:2019-05-03 19:14:37
【问题描述】:

运行以下代码:

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

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

【问题讨论】:

  • 解释这两种不同的执行模式,首先注意到它们是两种完全不同的语言。它们碰巧共享 一些 语法,但这就是相似之处。
  • 结果是:乱码。除非您正在参加 Java 混淆竞赛,否则最好不要模仿。
  • @TheGreatDuck 经过您的编辑,问题仍然有效,不再重复,但投票最多的答案包含很多不相关的部分(以及“两次执行mode”语句不再有意义)。

标签: java c language-lawyer variable-assignment side-effects


【解决方案1】:

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

来自C11 6.5p22:

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

== 左侧的i 是对标量对象i 的值进行值计算,而右侧i = 2 具有赋值2 的副作用到i== 的 LHS 和 RHS 未排序 w.r.t。彼此。因此整个程序在 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 中是正确的。在评估对 RHS 的副作用之前,严格确定 LHS 的值。

在 C 中,您必须将其写成类似

newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;

【讨论】:

  • 为什么 LHS 上的 i 是一个值计算?它是否在标准中的某处指定?
  • @SomeName 当然是在标准中的某处指定的。 :D
  • 那么参考呢? :) 我唯一能找到的是 3.19 中定义的 value 术语,但那里没有定义 value computation
  • @SomeName 您需要从散文中的太多地方推断它:/ footnote 84 但是可以用作快捷方式。如果i 没有表达式的值,则将定义a[i++] = i;。脚注虽然不是规范的。
  • @SomeName 5.1.2.3p2 表示“左值表达式的值计算”,这里是一个例子,i 是一个左值,它的值需要计算,因为它不是一个操作数。 6.3.2.1p2 表示“转换为存储在指定对象中的值”,但这是 5.1.2.3p2 所指的那个
【解决方案2】:

Java Language Specification (§15.7) 声明:

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

specification (§15.21.1) 还声明:

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

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

if (1 == 2) {

}

在 C 中,它只是未定义的(参见 Antti's answer)。

【讨论】:

    【解决方案3】:

    在 C 中,i == (i = 2) 的行为是未定义的,因为它试图更新一个对象并在计算中使用该对象的值,而没有中间的序列点。结果会因编译器、编译器设置甚至周围的代码而异。

    【讨论】:

      猜你喜欢
      • 2011-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 2015-08-17
      • 2021-09-20
      相关资源
      最近更新 更多