¿Qué es x después de "x = x ++"?

285

¿Qué sucede (detrás de las cortinas) cuando se ejecuta esto?

int x = 7;
x = x++;

Es decir, cuando una variable se incrementa y se asigna a sí misma en una declaración? Compilé y ejecuté esto. xsigue siendo 7 incluso después de toda la declaración . En mi libro, dice que xse incrementa!

Miguel
fuente
9
Pruebe esto: int x = 7; x = ++x;por supuesto, sigue siendo un código horrible, no necesita reasignarlo. int x = 7; x++;es suficiente.
stivlo
77
Esta es una práctica realmente mala, no incremente la variable en la misma línea que la usa.
Yousf
55
Prefiero usar x += 1, excepto tal vez en bucles. for(int x=0; x<7; x++)
Svish
2
@andyortlieb no hay ningún objeto, solo un valor básico.
fortran

Respuestas:

301

xse incrementa. Pero está asignando el antiguo valor de xvolver a sí mismo.


x = x++;
  1. x++incrementa xy devuelve su valor anterior.
  2. x = asigna el antiguo valor de nuevo a sí mismo.

Entonces, al final, xse le asigna de nuevo a su valor inicial.

Místico
fuente
3
Entonces, ¿qué dirás sobre x = ++ x;
Hisham Muneer
3
@HishamMuneer xse incrementa primero antes de que se lea en ese caso, por lo que terminas con x + 1.
@HishamMuneer Es muy tarde. Pero lo pongo aquí porque puede ser útil para otras personas que lo verán en el futuro. La mejor manera de entender este problema es mirar el código de ensamblaje creado para x = x ++ y x = ++ x. Por favor, vea la respuesta de Thinkingcap también.
nantitv
Sé que esto es muy viejo, pero tengo una pregunta. ¿El orden de operación anterior está garantizado por la norma? ¿Es posible que la asignación se ejecute antes del incremento?
Arma Esmeralda
@EmeraldWeapon Está definido en Java. Solo en C / C ++ ves ese tipo de travesuras.
Mysticial
385
x = x++;

es equivalente a

int tmp = x;
x++;
x = tmp;
Príncipe John Wesley
fuente
46
Lol, yay para definiciones recursivas. probablemente x=x+1x++
debiste
8
@ user606723: No. Me refería a la declaración completa x = x++, no solo al incremento de la publicación x++.
Príncipe John Wesley
20
No creo que esto sea tan útil sin más explicaciones. Por ejemplo, no es cierto que x = ++x;también sea equivalente a int tmp = x; ++x; x = tmp;, entonces, ¿con qué lógica podemos deducir que su respuesta es correcta (cuál es)?
kvb
44
aún más claro está en asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker
3
@forker: Creo que sería más claro si usara las instrucciones de ensamblaje que se aplican al procesador que está usando Michael;)
Carl
258

La declaración:

x = x++;

es equivalente a:

tmp = x;   // ... this is capturing the value of "x++"
x = x + 1; // ... this is the effect of the increment operation in "x++" which
           //     happens after the value is captured.
x = tmp;   // ... this is the effect of assignment operation which is
           //     (unfortunately) clobbering the incremented value.

En resumen, la declaración no tiene ningún efecto.

Los puntos clave:

  • El valor de una expresión de incremento / decremento de Postfix es el valor del operando antes de que tenga lugar el incremento / decremento. (En el caso de un formulario de Prefijo, el valor es el valor del operando después de la operación)

  • el RHS de una expresión de asignación se evalúa por completo (incluidos los incrementos, decrementos y / u otros efectos secundarios) antes de asignar el valor al LHS.

Tenga en cuenta que, a diferencia de C y C ++, el orden de evaluación de una expresión en Java está totalmente especificado y no hay espacio para la variación específica de la plataforma. Los compiladores solo pueden reordenar las operaciones si esto no cambia el resultado de ejecutar el código desde la perspectiva del hilo actual. En este caso, a un compilador se le permitiría optimizar la declaración completa porque se puede demostrar que no funciona.


En caso de que no sea ya obvio:

  • "x = x ++;" es casi seguro un error en cualquier programa.
  • El OP (¡para la pregunta original!) Probablemente significaba "x ++;" en lugar de "x = x ++;".
  • Las declaraciones que combinan auto inc / decrement y asignación en la misma variable son difíciles de entender y, por lo tanto, deben evitarse independientemente de su corrección . Simplemente no hay necesidad de escribir código como ese.

Con suerte, los verificadores de códigos como FindBugs y PMD marcarán códigos como este como sospechosos.

Stephen C
fuente
77
Como nota al margen, OP, probablemente quieras decir simplemente en x++lugar de x = x++.
Jon Newmuis
3
Correcto, pero quizás enfatice que el incremento ocurre después de la evaluación de la expresión a la derecha, pero antes de la asignación al lado izquierdo, de ahí la aparente "sobrescritura"
Bohemio
2
ese parece ser uno de esos trabalenguas de la escuela secundaria ... ¡bueno para aclarar lo básico!
kumarharsh
1
@Alberto - Es bueno saber que no tomas las declaraciones de "expertos" como "verdad del evangelio". Sin embargo, una mejor manera de validar lo que dije sería consultar el JLS. Su prueba de compilación / descompilación solo muestra que lo que dije es válido para un compilador de Java. Otros podrían (hipotéticamente) comportarse de manera diferente ... excepto que el JLS no lo permite.
Stephen C
44
Solo un FYI: esto se publicó originalmente en una pregunta diferente, que se cerró como un duplicado de esta y ahora se ha fusionado.
Shog9
33
int x = 7;
x = x++;

Tiene un comportamiento indefinido en C y para Java vea esta respuesta . Depende del compilador lo que suceda.

usuario712092
fuente
44
No, no depende del compilador de acuerdo con la respuesta que citó - edite - -1 por ahora
Mr_and_Mrs_D
@Mr_and_Mrs_D ¿Entonces depende de qué?
Mac
2
Es un comportamiento indefinido solo para C_. Aun así, decir que depende del compilador es engañoso, implica que el compilador debería especificar este comportamiento. Revoco mi voto, pero considero editar su respuesta. Editar: ¡Uy, no puedo! Tienes que editarlo primero: D
Mr_and_Mrs_D
16

Una construcción como x = x++;indica que probablemente estás malinterpretando lo ++que hace el operador:

// original code
int x = 7;
x = x++;

Reescribamos esto para hacer lo mismo, basándonos en eliminar el ++operador:

// behaves the same as the original code
int x = 7;
int tmp = x; // value of tmp here is 7
x = x + 1; // x temporarily equals 8 (this is the evaluation of ++)
x = tmp; // oops! we overwrote y with 7

Ahora, reescribamos para hacer (lo que creo) que querías:

// original code
int x = 7;
x++;

La sutileza aquí es que el ++operador modifica la variablex , a diferencia de una expresión como x + x, que evaluaría a un valor int pero dejaría la variable en xsí misma sin cambios. Considere una construcción como el venerable forbucle:

for(int i = 0; i < 10; i++)
{
    System.out.println(i);
}

Observe el i++de allí? Es el mismo operador. Podríamos reescribir este forbucle así y se comportaría igual:

for(int i = 0; i < 10; i = i + 1)
{
    System.out.println(i);
}

También recomiendo no usar el ++operador en expresiones más grandes en la mayoría de los casos. Debido a la sutileza de cuando modifica la variable original en el incremento previo versus posterior ( ++xy x++, respectivamente), es muy fácil introducir errores sutiles que son difíciles de rastrear.

FMM
fuente
13

Según el código de bytes obtenido de los archivos de clase,

Ambas asignaciones incrementan x, pero la diferencia es el tiempo de when the value is pushed onto the stack

En Case1, Push ocurre (y luego se asigna) antes del incremento (esencialmente significa que su incremento no hace nada)

En Case2, el incremento ocurre primero (convirtiéndolo en 8) y luego empujado a la pila (y luego asignado a x)

Caso 1:

int x=7;
x=x++;

Código de bytes:

0  bipush 7     //Push 7 onto  stack
2  istore_1 [x] //Pop  7 and store in x
3  iload_1  [x] //Push 7 onto stack
4  iinc 1 1 [x] //Increment x by 1 (x=8)
7  istore_1 [x] //Pop 7 and store in x
8  return       //x now has 7

Caso 2:

int x=7; 
x=++x;

Código de bytes

0  bipush 7     //Push 7 onto stack
2  istore_1 [x] //Pop 7 and store in x
3  iinc 1 1 [x] //Increment x by 1 (x=8)
6  iload_1  [x] //Push x onto stack
7  istore_1 [x] //Pop 8 and store in x
8  return       //x now has 8
  • La pila aquí se refiere a la pila de operandos, local: índice x: 1 tipo: int
pareja
fuente
¿Puede explicar su respuesta en detalle?
Nihar
Por favor, eche un vistazo al enlace de referencia y los comentarios
incluso antes del
8

Se incrementa después de " x = x++;". Sería 8 si lo hicieras " x = ++x;".

FJ
fuente
44
Si se incrementa después x = x++, entonces debería ser 8.
R. Martinho Fernandes
8

El operador de incremento posterior funciona de la siguiente manera:

  1. Almacenar el valor anterior del operando.
  2. Incremente el valor del operando.
  3. Devuelve el valor anterior del operando.

Entonces la declaración

int x = 7;
x = x++; 

se evaluaría de la siguiente manera:

  1. x se inicializa con el valor 7
  2. El operador de incremento posterior almacena el valor anterior de x, es decir, 7 para devolver.
  3. Incrementa la x, así que ahora x es 8
  4. Devuelve el valor anterior de x, es decir, 7 y se asigna de nuevo a x, por lo que x vuelve a ser 7

Por lo tanto, x aumenta, pero dado que x ++ está asignando el resultado nuevamente a x, el valor de x se anula a su valor anterior.

GauravLuthra
fuente
Pero en msvc x es 8. Sí en gcc y clang x es 7.
Summer Sun
7

El incremento ocurre después de que se llama x, por lo que x todavía es igual a 7. ++ x sería igual a 8 cuando se llama x

JonPM
fuente
7

Cuando reasigne el valor x, todavía es 7. Pruebe x = ++xy obtendrá 8 más.

x++; // don't re-assign, just increment
System.out.println(x); // prints 8
Vishal
fuente
6

porque x ++ incrementa el valor DESPUÉS de asignarlo a la variable. etc. y durante la ejecución de esta línea:

x++;

la varialbe x seguirá teniendo el valor original (7), pero usando x nuevamente en otra línea, como

System.out.println(x + "");

te dará 8.

si desea usar un valor incrementado de x en su declaración de asignación, use

++x;

Esto incrementará x en 1, ENTONCES asigne ese valor a la variable x.

[Editar] en lugar de x = x ++, es solo x ++; el primero asigna el valor original de x a sí mismo, por lo que en realidad no hace nada en esa línea.

josephus
fuente
El que dice que aumenta después de asignar, y el que dice que imprimirá 8. Incrementa antes de asignar, e imprime 7.
R. Martinho Fernandes
si x es originalmente 7, System.out.println (String.valueOf (x ++)); impresiones 7. ¿estás seguro de que estamos hablando del mismo lenguaje de programación?
Joseph
Sí lo soy. Este ideone.com/kj2UU no imprime 8, como dice esta respuesta.
R. Martinho Fernandes
Sí, estaba equivocado. x = x ++ asignará 7 a x primero antes de incrementar x. como x ++ (que es una asignación en sí misma) se resuelve primero antes de x = (lo que sea), el valor asignado a x en x = (lo que sea) seguirá. lo siento, no vi eso.
Joseph
1
En realidad, el incremento es lo primero que sucede. ideone.com/xOIDU
R. Martinho Fernandes
4

¿Qué pasa cuando int x = 7; x = x++;?

ans -> x++significa el primer valor de uso de x para la expresión y luego aumentarlo en 1.
Esto es lo que sucede en su caso. El valor de x en RHS se copia en la variable x en LHS y luego el valor de xse incrementa en 1.

Del mismo modo ++x significa ->aumentar el valor de x primero en uno y luego usarlo en la expresión.
Entonces, en su caso, si lo hace x = ++x ; // where x = 7
, obtendrá un valor de 8.

Para mayor claridad, intente averiguar cuántas sentencias printf ejecutarán el siguiente código

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend
muestra
fuente
no es correcto "El valor de x en RHS se copia en la variable x en LHS y luego el valor de x aumenta en 1" - esto sería x8, pero es 7 - se produce un incremento entre la lectura y la asignación
usuario85421
3

++xes pre-incremento ->x se incrementa antes de ser usado
x++es post-incremento ->x se incrementa después de ser usado

int x = 7; -> x get 7 value <br>
x = x++; -> x get x value AND only then x is incremented
Roberto Martelloni
fuente
1

Entonces esto significa: x++no es igual ax = x+1

porque:

int x = 7; x = x++;
x is 7

int x = 7; x = x = x+1;
x is 8

y ahora parece un poco extraño:

int x = 7; x = x+=1;
x is 8

muy dependiente del compilador!

linuxeasy
fuente
2
¿Quién dijo que era igual en primer lugar?
fortran
1
Si yo fuera tú, tiraría a la basura estos libros inmediatamente xD En cualquier caso, sería como (x = x + 1, x-1)en C, donde se permiten expresiones separadas por comas.
fortran
3
@fortran: Bueno, en mi copia de una década de "El lenguaje de programación Java, tercera edición" en la página 159 dice "" La expresión i ++ es equivalente a i = i + 1, excepto que me evalúan solo una vez ". en primer lugar, James Gosling, parecería. Esta parte de esta edición de la especificación de Java es extraordinariamente vaga y poco especificada; supongo que ediciones posteriores limpiaron el lenguaje para expresar la semántica real del operador más claramente.
Eric Lippert
2
@fortran: por "excepto que se evalúa solo una vez", el estándar intenta transmitir que una expresión como "M (). x ++" solo llama a M () una vez. Una redacción menos vaga y más precisa enfatizaría que hay una diferencia entre evaluar i como una variable para determinar su ubicación de almacenamiento , que es lo que significa "evaluado solo una vez" aquí, y leer o escribir en esa ubicación de almacenamiento . - cualquiera de los cuales podría ser una interpretación razonable pero incorrecta de 'evaluado'. ¡Claramente, la ubicación de almacenamiento debe ser leída y escrita!
Eric Lippert
1
"muy dependiente del compilador" - ¡En absoluto!
Stephen C
-1

x = x ++;

Este es el operador posterior al incremento. Debe entenderse como "Usar el valor del operando y luego incrementar el operando".

Si desea que ocurra lo contrario, es decir, "Incremente el operando y luego use el valor del operando", debe usar el operador de incremento previo como se muestra a continuación.

x = ++ x;

Este operador primero incrementa el valor de x en 1 y luego asigna el valor nuevamente a x.

deepak
fuente
-1

Creo que esta controversia se puede resolver sin entrar en código y solo pensar.

Considere i ++ y ++ i como funciones, digamos Func1 y Func2.

Ahora i = 7;
Func1 (i ++) devuelve 7, Func2 (++ i) devuelve 8 (todo el mundo lo sabe). Internamente, ambas funciones aumentan de i a 8, pero devuelven valores diferentes.

Entonces i = i ++ llama a la función Func1. Dentro de la función i se incrementa a 8, pero al finalizar la función devuelve 7.

Entonces, en última instancia, 7 se asigna a i. (Entonces, al final, i = 7)

Pranav Mahajan
fuente
2
No hay una "controversia" válida aquí. El código se comporta de manera demostrable de una manera particular, y el comportamiento se ajusta al JLS. Cualquiera que piense que se comporta de manera diferente o no lo ha intentado, o está engañado. (Esto es un poco como decir que 7 x 7 es 49 es "controvertido" cuando alguien olvida sus tablas de tiempos ...)
Stephen C
-2

Esto se debe a que utilizó un operador posterior al incremento. En esta siguiente línea de código

x = x++;

Lo que sucede es que estás asignando el valor de x a x. x ++ incrementa x después de que el valor de x se asigna a x. Así es como funcionan los operadores posteriores al incremento. Funcionan después de que se haya ejecutado una declaración. Entonces, en su código, x se devuelve primero después y luego se incrementa.

Si lo hiciste

x = ++x;

La respuesta sería 8 porque usó el operador de pre-incremento. Esto incrementa el valor primero antes de devolver el valor de x.

Luego
fuente