java c language-lawyer variable-assignment side-effects

java - ¿Cuál es el resultado de i==(i=2)?



language-lawyer variable-assignment (3)

El comportamiento de un programa en C que ejecuta la expresión i == (i = 2) no está definido .

Viene de C11 6.5p22 :

  1. Si un efecto secundario en un objeto escalar no tiene secuencia en relación con un efecto secundario diferente en el mismo objeto escalar o un cálculo de valor utilizando el valor del mismo objeto escalar, el comportamiento no está definido. Si hay varios ordenamientos permitidos de las subexpresiones de una expresión, el comportamiento no está definido si se produce un efecto secundario no secuencial en cualquiera de los ordenamientos.84)

La i en el lado izquierdo de == es un cálculo de valor sobre el valor del objeto escalar i y el lado derecho i = 2 tiene un efecto secundario de asignar el valor 2 a i . El LHS y el RHS de == están secuenciados entre sí. Por lo tanto, todo el programa no tiene sentido en C.

Compile con gcc -Wall y GCC gcc -Wall :

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

A diferencia de C, Java garantiza el orden de evaluación de los operandos (de izquierda a derecha), por lo tanto

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

Es correcto en Java. El valor de LHS se determina estrictamente antes de que ocurra la evaluación del efecto secundario en el RHS.

En C tienes que escribir esto como algo así como

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

Ejecuta el siguiente código:

// In Java, output ##### public static void main(String[] args) { int i = 1; if(i == (i = 2)) { System.out.println("@@@@@"); } else { System.out.println("#####"); } }

Pero:

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

La motivación para hacer esta pregunta viene del siguiente código:

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

Entonces, ¿cómo explicar los dos modos de ejecución diferentes anteriores?


En C, el comportamiento de i == (i = 2) no está definido porque intenta actualizar un objeto y usar el valor de ese objeto en un cálculo sin un punto de secuencia de intervención. El resultado variará según el compilador, la configuración del compilador, incluso el código que lo rodea.


La especificación del lenguaje Java (§15.7) establece:

El lenguaje de programación Java garantiza que los operandos de los operadores parecen ser evaluados en un orden de evaluación específico, es decir, de izquierda a derecha.

La especificación (§15.21.1) también establece que:

El valor producido por el operador == es true si el valor del operando de la izquierda es igual al valor del operando de la derecha; De lo contrario, el resultado es false .

Por lo tanto, en Java, la instrucción if en tiempo de ejecución se vería como la siguiente, que obviamente se evalúa como false :

if (1 == 2) { }

En C, es simplemente indefinido (ver la respuesta de Antti ).