Tengo el siguiente código:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
Sabemos que debería haber escrito solo x++o x=x+1, pero x = x++primero debería atribuirse xa sí mismo y luego incrementarlo. ¿Por qué xcontinúa con 0como valor?
--actualizar
Aquí está el código de bytes:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
Leeré sobre las instrucciones para tratar de entender ...

x++es post-incremento;x=es la asignación del resultado ; el resultado dex++es el originalx(y hay un efecto secundario de incremento, pero eso no cambia el resultado), así que esto puede interpretarse comovar tmp = x; x++; x = tmp;Respuestas:
Nota : Originalmente publiqué el código C # en esta respuesta con fines ilustrativos, ya que C # le permite pasar
intparámetros por referencia con larefpalabra clave. Decidí actualizarlo con un código Java legal real usando la primeraMutableIntclase que encontré en Google para aproximarme a lo querefhace en C #. Realmente no puedo decir si eso ayuda o perjudica la respuesta. Diré que personalmente no he hecho tanto desarrollo de Java; por lo que sé, podría haber formas mucho más idiomáticas de ilustrar este punto.Quizás si escribimos un método para hacer el equivalente de lo
x++que hace, esto lo aclarará más.¿Derecha? Incremente el valor pasado y devuelva el valor original: esa es la definición del operador postincremento.
Ahora, veamos cómo se desarrolla este comportamiento en su código de ejemplo:
postIncrement(x)que hace Incrementosx, sí. Y luego devuelve lo quexestaba antes del incremento . Este valor de retorno se asigna ax.Entonces, el orden de los valores asignados
xes 0, luego 1, luego 0.Esto podría ser más claro aún si reescribimos lo anterior:
Su fijación en el hecho de que cuando reemplaza
xen el lado izquierdo de la asignación anterior cony"puede ver que primero incrementa x, y luego lo atribuye a y" me confunde. No es a loxque se le está asignandoy; es el valor anteriormente asignadox. Realmente, la inyecciónyhace que las cosas no sean diferentes del escenario anterior; simplemente tenemos:Entonces está claro:
x = x++efectivamente no cambia el valor de x. Siempre hace que x tenga los valores x 0 , luego x 0 + 1 y luego x 0 nuevamente.Actualización : Incidentalmente, para que no dude que
xalguna vez se asigna a 1 "entre" la operación de incremento y la asignación en el ejemplo anterior, he preparado una demostración rápida para ilustrar que este valor intermedio realmente "existe", aunque lo hará nunca ser "visto" en el hilo de ejecución.La demostración llama
x = x++;en un bucle mientras un hilo separado imprime continuamente el valor dexen la consola.A continuación se muestra un extracto de la salida del programa anterior. Observe la ocurrencia irregular de 1s y 0s.
fuente
Integerclase, que es parte de la biblioteca estándar, e incluso tiene la ventaja de estar en caja automática desde y haciaintcasi de forma transparente.xen su último ejemplo debe declararsevolatile, de lo contrario, es un comportamiento indefinido y ver1s es específico de la implementación.++podría hacer antes o después de la asignación. Prácticamente hablando, puede haber un compilador que haga lo mismo que Java, pero no querrás apostar por él.x = x++funciona de la siguiente manera:x++. La evaluación de esta expresión produce un valor de expresión (que es el valor dexantes del incremento) e incrementosx.x, sobrescribiendo el valor incrementadoEntonces, la secuencia de eventos se ve de la siguiente manera (es un bytecode descompilado real, como el producido por
javap -c, con mis comentarios):A modo de comparación
x = ++x:fuente
iincincrementa una variable, no incrementa un valor de pila, ni deja un valor en la pila (a diferencia de casi cualquier otra operación aritmética). Es posible que desee agregar el código generado por++xpara la comparación.Esto sucede porque el valor de
xno se incrementa en absoluto.es equivalente a
Explicación:
Veamos el código de byte para esta operación. Considere una clase de muestra:
Ahora ejecutando el desensamblador de clase en esto obtenemos:
Ahora, la máquina virtual Java está basada en la pila, lo que significa que para cada operación, los datos se enviarán a la pila y desde la pila, los datos saldrán para realizar la operación. También hay otra estructura de datos, típicamente una matriz para almacenar las variables locales. Las variables locales reciben identificadores que son solo los índices de la matriz.
Veamos la mnemotecnia en el
main()método:iconst_0: El valor constante0se transfiere a la pila.istore_1: El elemento superior de la pila aparece y se almacena en la variable local con el índice1que es
x.iload_1: El valor en la ubicación1cuyo valorxes0, se inserta en la pila.iinc 1, 1: El valor en la ubicación de la memoria1se incrementa en1. Entoncesxahora se convierte1.istore_1: El valor en la parte superior de la pila se almacena en la ubicación de la memoria1. Eso se0asigna axsobrescribir su valor incrementado.Por lo tanto, el valor de
xno cambia dando como resultado el bucle infinito.fuente
++), pero la variable se sobrescribe más tarde.int temp = x; x = x + 1; x = temp;es mejor no usar una tautología en su ejemplo.Sin embargo, "
=" tiene una prioridad de operador menor que "++".Entonces
x=x++;debe evaluar de la siguiente maneraxpreparado para la asignación (evaluado)xincrementadoxasignado ax.fuente
++tiene mayor prioridad que=en C y C ++, pero la declaración no está definida en esos lenguajes.Ninguna de las respuestas fue acertada, así que aquí va:
Cuando estás escribiendo
int x = x++, no estás asignandoxser él mismo en el nuevo valor, estás asignandoxser el valor de retorno de lax++expresión. Que resulta ser el valor original dex, como se insinúa en la respuesta de Colin Cochrane .Por diversión, pruebe el siguiente código:
El resultado será
El valor de retorno de la expresión es el valor inicial de
x, que es cero. Pero más tarde, cuando leemos el valor dex, recibimos el valor actualizado, ese es uno.fuente
Ya ha sido explicado bien por otros. Solo incluyo los enlaces a las secciones de especificación de Java relevantes.
x = x ++ es una expresión. Java seguirá el orden de evaluación . Primero evaluará la expresión x ++, que incrementará x y establecerá el valor del resultado al valor anterior de x . Luego asignará el resultado de la expresión a la variable x. Al final, x vuelve a su valor anterior.
fuente
Esta declaración:
evalúa así:
xsobre la pila;x;xde la pila.Entonces el valor no ha cambiado. Compare eso con:
que se evalúa como:
x;xsobre la pila;xde la pila.Lo que quieres es:
fuente
x++fragmento de código.x++;su soluciónx=x; x++;y está haciendo lo que dice que está haciendo el código original.La respuesta es bastante directa. Tiene que ver con el orden en que se evalúan las cosas.
x++devuelve el valor yxluego incrementax.En consecuencia, el valor de la expresión
x++es0. Entonces estás asignandox=0cada vez en el ciclo. Ciertamentex++incrementa este valor, pero eso sucede antes de la asignación.fuente
De http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
Para ilustrar, intente lo siguiente:
Que imprimirá 1 y 0.
fuente
Efectivamente está obteniendo el siguiente comportamiento.
La idea es que el operador posterior al incremento (x ++) incremente esa variable en cuestión DESPUÉS de devolver su valor para usar en la ecuación en la que se usa.
Editar: agregando un poco debido al comentario. Considérelo como lo siguiente.
fuente
Realmente no necesita el código de la máquina para comprender lo que está sucediendo.
Según las definiciones:
El operador de asignación evalúa la expresión del lado derecho y la almacena en una variable temporal.
1.1. El valor actual de x se copia en esta variable temporal
1.2. x se incrementa ahora.
La variable temporal se copia en el lado izquierdo de la expresión, que es x por casualidad. Por eso, el antiguo valor de x se copia nuevamente en sí mismo.
Es bastante simple
fuente
Esto se debe a que nunca se incrementa en este caso.
x++usará su valor primero antes de incrementar como en este caso será como:Pero si haces
++x;esto aumentará.fuente
El valor permanece en 0 porque el valor de
x++es 0. En este caso, no importa si el valor dexse incrementa o no, la asignaciónx=0se ejecuta. Esto sobrescribirá el valor temporal incrementado dex(que fue 1 por "muy poco tiempo").fuente
x++, no para toda la tareax=x++;Esto funciona como espera que el otro lo haga. Es la diferencia entre prefijo y postfix.
fuente
Piense en x ++ como una llamada de función que "devuelve" lo que X era antes del incremento (es por eso que se llama un incremento posterior).
Por lo tanto, el orden de operación es:
1: almacena en caché el valor de x antes de incrementar
2: incrementa x
3: devuelve el valor almacenado en caché (x antes de que se incremente)
4: el valor de retorno se asigna a x
fuente
Cuando ++ está en rhs, el resultado se devuelve antes de que se incremente el número. Cambie a ++ x y hubiera estado bien. Java habría optimizado esto para realizar una sola operación (la asignación de x a x) en lugar del incremento.
fuente
Bueno, hasta donde puedo ver, el error ocurre, debido a que la asignación anula el valor incrementado, con el valor anterior al incremento, es decir, deshace el incremento.
Específicamente, la expresión "x ++" tiene el valor de 'x' antes del incremento en oposición a "++ x" que tiene el valor de 'x' después del incremento.
Si está interesado en investigar el código de bytes, echaremos un vistazo a las tres líneas en cuestión:
7: iload_1 # Pondrá el valor de la 2da variable local en la pila
8: iinc 1,1 # incrementará la 2da variable local con 1, ¡tenga en cuenta que deja la pila intacta!
9: istore_1 # Aparecerá la parte superior de la pila y guardará el valor de este elemento en la segunda variable local
(Puede leer los efectos de cada instrucción JVM aquí )
Esta es la razón por la cual el código anterior se repetirá indefinidamente, mientras que la versión con ++ x no lo hará. El bytecode para ++ x debería ser bastante diferente, por lo que recuerdo del compilador 1.3 Java que escribí hace poco más de un año, el bytecode debería ser algo así:
Entonces, simplemente intercambiando las dos primeras líneas, cambia la semántica para que el valor que queda en la parte superior de la pila, después del incremento (es decir, el "valor" de la expresión) sea el valor después del incremento.
fuente
Entonces:
Mientras
Entonces:
Por supuesto, el resultado final es el mismo que solo
x++;o++x;en una línea por sí mismo.fuente
debido a la declaración anterior x nunca llega a 3;
fuente
Me pregunto si hay algo en la especificación de Java que defina con precisión el comportamiento de esto. (La implicación obvia de esa afirmación es que soy demasiado vago para verificarlo).
Nota del código de bytes de Tom, las líneas clave son 7, 8 y 11. La línea 7 carga x en la pila de cálculo. La línea 8 incrementa x. La línea 11 almacena el valor de la pila de nuevo a x. En los casos normales en los que no se asignan valores a sí mismos, no creo que haya alguna razón por la que no pueda cargar, almacenar e incrementar. Obtendría el mismo resultado.
Como, supongamos que tiene un caso más normal en el que escribió algo como: z = (x ++) + (y ++);
Si dijo (pseudocódigo para omitir tecnicismos)
o
debería ser irrelevante Cualquiera de las dos implementaciones debería ser válida, creo.
Sería extremadamente cauteloso al escribir código que depende de este comportamiento. Me parece muy dependiente de la implementación, entre las grietas en las especificaciones. La única vez que marcaría la diferencia es si hiciste algo loco, como en el ejemplo aquí, o si tienes dos hilos ejecutándose y dependías del orden de evaluación dentro de la expresión.
fuente
Creo que porque en Java ++ tiene una precedencia más alta que = (asignación) ... ¿Lo hace? Mire http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...
De la misma manera, si escribe x = x + 1 ... + tiene una precedencia mayor que = (asignación)
fuente
++tiene mayor prioridad que=en C y C ++ también, pero la declaración no está definida.La
x++expresión se evalúa comox. La++parte afecta el valor después de la evaluación , no después de la declaración . por lo quex = x++se traduce efectivamente enfuente
Antes de incrementar el valor en uno, el valor se asigna a la variable.
fuente
Está sucediendo porque se incrementó después. Significa que la variable se incrementa después de evaluar la expresión.
x es ahora 10, pero y es 9, el valor de x antes de que se incrementara.
Ver más en Definición de incremento posterior .
fuente
x/yejemplo es diferente del código real, y la diferencia es relevante. Su enlace ni siquiera menciona Java. Para dos de los idiomas que no menciona, la declaración en la pregunta no está definido.Comprueba el siguiente código,
la salida será
post incrementsignifica incrementar el valor y devolver el valor antes del incremento . Es por ello que el valortempes0. Entonces, ¿quétemp = ipasa si esto está en un bucle (excepto la primera línea de código)? justo como en la pregunta !!!!fuente
El operador de incremento se aplica a la misma variable a la que está asignando. Eso es pedir problemas. Estoy seguro de que puede ver el valor de su variable x mientras ejecuta este programa ... eso debería dejar en claro por qué el ciclo nunca termina.
fuente