次のコードを実行します。
// 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()));
}
}
それでは、上記の2つの異なる実行モードをどのように説明するのでしょうか?
式i == (i = 2)
を実行するCプログラムのbehaviourは、undefinedです。
- スカラーオブジェクトの副作用が、同じスカラーオブジェクトの異なる副作用、または同じスカラーオブジェクトの値を使用した値の計算のいずれかに対して順序付けられていない場合、動作は未定義です。式の部分式の許容される順序が複数ある場合、そのような順序付けられていない副作用がいずれかの順序で発生する場合、動作は未定義です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では正しい。 LHSの副作用の評価が行われる前に、LHSの値は厳密に決定されます。
Cでは、haveを次のように記述します
newPrev = get();
haveNext = (prev == newPrev);
prev = newPrev;
Java言語仕様(§15.7) 状態:
Javaプログラミング言語は、演算子のオペランドが特定の評価順序、つまり左から右に評価されるように見えることを保証します。
仕様(§15.21.1) は次のことも述べています:
左側のオペランドの値が右側のオペランドの値と等しい場合、
==
演算子によって生成される値はtrue
です。それ以外の場合、結果はfalse
です。
したがって、Javaでは、実行時のif文は次のようになり、明らかにfalse
と評価されます。
if (1 == 2) {
}
Cでは、単に未定義です( Anttiの答え を参照)。
Cでは、i == (i = 2)
の動作は未定義です。これは、オブジェクトを更新し、シーケンスポイントを介在させずにそのオブジェクトの値を計算で使用しようとするためです。結果は、コンパイラ、コンパイラ設定、さらには周囲のコードによって異なります。