En el código veo muchas veces x++
en bucles, pero alguna vez me encuentro ++x
.
¿Hay alguna diferencia entre estas dos expresiones?
En el código veo muchas veces x++
en bucles, pero alguna vez me encuentro ++x
.
¿Hay alguna diferencia entre estas dos expresiones?
x++
, El valor de la expresión es el valor de la variable antes que el nuevo valor sea almacenado.
Ejemplo:
int x = 1;
System.out.println(x++); // imprime 1.
System.out.println(x); // imprime 2.
++x
, El valor de la expresión es el valor de la variable después que el nuevo valor es almacenado.
Ejemplo:
int x = 1;
System.out.println(++x); // imprime 2.
System.out.println(x); // imprime 2.
Similar para x--
y --x
Ejemplos:
int x = 1;
System.out.println(x--); // imprime 1.
System.out.println(x); // imprime 0.
int x = 1;
System.out.println(--x); // imprime 0.
System.out.println(x); // imprime 0.
Más información:
En el codigo veo muchas veces
x++
en loops, pero alguna vez me encuentro++x
.
Si te refieres a construcciones de este tipo:
public static void main(String[] args) {
for (int i = 0; i < 10; ++i) {
System.out.println(i);
}
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
... entonces no hay diferencia. Si vemos el bytecode:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: iconst_0
22: istore_1
23: iload_1
24: bipush 10
26: if_icmpge 42
29: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
32: iload_1
33: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
36: iinc 1, 1
39: goto 23
42: return
En ambos casos, justo después de invocar el println
( invokevirtual #3
), se incrementa la variable 1 (en el código es i
para ambos casos) en 1. Véase el iinc 1, 1
antes del goto
.
No obstante, si te refieres a algo como esto:
public static void main(String[] args) {
for (int i = 0; i < 10;) {
System.out.println(i++);
}
for (int i = 0; i < 10;) {
System.out.println(++i);
}
}
... entonces sí hay diferencia. Revisando el bytecode:
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: iinc 1, 1
15: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
18: goto 2
21: iconst_0
22: istore_1
23: iload_1
24: bipush 10
26: if_icmpge 42
29: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
32: iinc 1, 1
35: iload_1
36: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
39: goto 23
42: return
En el primer caso, primero se carga el valor de la variable 1 (i
) para ser usado en el println
. Véase el iload_1
. Después se incrementa esta variable en 1. Véase el iinc 1, 1
.
En el segundo caso, primero se incrementa el valor de la variable 1 (i
) en 1, Véase el iinc 1, 1
. Luego se carga el valor de la variable 1 (i
) para ser usado en el println
. Véase el iload_1
.
La diferencia no está en que el incremento se ejecute antes o después de evaluar la expresión sino en el valor de retorno de las dos expresiones.
Según la especificación oficial de Java en la sección: 15.14.2. Postfix Increment Operator ++ y 15.15.1. Prefix Increment Operator ++
15.14.2. Postfix Increment Operator ++
...The value of the postfix increment expression is the value of the variable before the new value is stored.
Traducción
15.14.2. Operador de Incremento Postfijo ++
El valor de la expresión de incremento postfijo es el valor de la variable antes que el nuevo valor sea almacenado.
Igualmente
15.15.1. Prefix Increment Operator ++
The value of the prefix increment expression is the value of the variable after the new value is stored.
Traducción
15.15.1. Operador de Incremento Prefijo ++
El valor de la expresión de incremento prefijo es el valor de la variable después que el nuevo valor es almacenado.
Ambas incrementan la variable x
en 1. La diferencia es que la expresión ++x
devuelve el valor de la variable incrementada en cambio la expresión x++
devuelve el valor original de variable antes de ser incrementada.
Es decir:
int x = 5;
System.out.println(++x); // Imprime 6, x vale 6: El resultado de la expresión ++x es 6 y se imprime 6
System.out.println(x); // Imprime 6, x vale 6: x ya se incrementó por lo que devuelve 6
Mientras que:
int x = 5;
System.out.println(x++); // Imprime 5, x vale 6. La variable x ya es 6 pero el resultado de la expresión es 5 por lo que se imprime 5
System.out.println(x); // Imprime 6, x vale 6: x ya se incrementó por lo que devuelve 6
Sé que la pregunta está taggeada en Java, y desconozco la implementación en este lenguaje (aunque supongo que será similar), pero en C#, aunque lo normal e intuitivo es decir que "uno devuelve el valor antes de incrementar, y el otro después", y eso más o menos da el significado básico... la realidad es que la implementación no es así, y tiene una secuencia de eventos muy concreta.
Esto no es un problema si lo usamos sólo: x++;
no trae ningún problema y la explicación simple es válida... pero si nos metemos en multihilos, y operaciones largas con diferentes precedencias de operadores, la cosa cambia.
Como digo, desconozco cómo lo hace Java, pero en C#, la secuencia de cada comando está perfectamente definida (e imagino que en Java será similar):
Para el tipo de prefijo (++x
), la secuencia es:
x
para producir la variablePara el tipo sufijo (x++
), la secuencia es:
x
para producir la variablePor tanto, la secuencia de eventos ocurre en ambos casos en el mismo orden, y no cambia ni la evaluación ni el incremento de la variable, lo único que cambia es el valor que se devuelve. Sólo hay diferencia en el paso 5.
En C# usando extensiones de métodos, esto es muy fácil de probar... en Java no lo tengo tan claro (sólo tengo nociones básicas de Java).
Perdón por haber contestado usando C#, estando la pregunta taggeada como Java, pero como ya digo, sospecho que la implementación debe ser similar, si no igual.
Ambas incrementan el valor de la variable en 1
. Si las utilizas en una linea donde sea la unica expresion no hay ninguna diferencia, pero si las ejecutas junto con otras hay una variacion importante.
++x lo hace antes de que se evalue la expresión. Tambien se llama PREINCREMENTO
Ejemplo:
int x = 5;
System.out.println(++x); // escribira 6 porque ejecutará x + 1 ANTES de printar
System.out.println(x); // volverá a escribir 6!!!!
x++ lo hace posteriormente a que la expresión sea evaluada. También se llama POSTINCREMENTO
Ejemplo:
int x = 5;
System.out.println(x++); // escribirá 5 porque ejecutará x + 1 DESPUÉS de printar
System.out.println(x); // escribirá 6
Este tipo de expresiones tambien es muy habitual al hacer asignaciones de un array cuando realizas un bucle sin contador (por ejemplo for-each) o rellenando varias propiedades de un mismo objeto ahorrando asi una linea de codigo y ganando legibilidad.
arrayALlenar[i++] = elemento;
en vez de
arrayALlenar[i] = elemento;
i++;
El pre y post incremento se definen en la Especificación del Lenguaje Java (JLS) en dos apartados de los que reproduzco traducidas las partes que son significativas para esta pregunta (énfasis mío) :
15.14.2:Operador de Incremento Postfijo ++
(..omitido..)
En tiempo de ejecución, si la evaluación de la expresión se completa abruptamente, entonces la expresión de incremento postfijo se completa abruptamente for la misma razón y no hay incremento. En caso contrario, el valor 1 se añade al valor de la variable y la suma es almacenada en la variable. (...omitido...) El valor de la expresión de incremento postfijo es el valor de la variable antes de que se almacene el nuevo valor.
(..omitido..)
15.15.1. Operador de Incremento Prefijo ++
(..omitido..)
En tiempo de ejecución, si la evaluación de la expresión se completa abruptamente, entonces la expresión de decremento prefijo se completa abruptamente for la misma razón y no hay incremento. En caso contrario, el valor 1 se añade al valor de la variable y la suma es almacenada en la variable. (...omitido...) El valor de la expresión de incremento postfijo es el valor de la variable después de que el nuevo valor sea almacenado.
(..omitido..)
Obsérvese que es todo igual, salvo el valor con que se evalúa la expresión, que es el que tenía la variable antes de que se almacenase el nuevo valor incrementado para el incremento postfijo y es el valor que tiene la variable después de que sea incrementado para el incremento prefijo.
Todo esto no tiene ninguna importancia en código como el siguiente :
for ( int n=1; n<10; ++n)
for ( int n=1; n<10; n++)
En ese código, para un mismo valor de n
, las expresiones ++n
y n++
se evalúan a valores distintos, pero esto es irrelevante puesto que este valor no es utilizado y lo único que tiene efecto significativo es el hecho de que la variable se incremente en 1, que sucede en ambos casos. Ambos bucles se ejecutarán el mismo número de iteraciones.
Pero sí es importante en código como el siguiente :
int a; int b;
a = 3;
b = a++;
System.out.println( "a="+a + " b=" + b);
a = 3;
b = ++a;
System.out.println( "a="+a + " b=" + b);
Donde la salida es :
a=4 b=3
a=4 b=4
En la expresión b = a++
se evalúa la variable a
(que vale 3), se añade 1 a este valor y el resultado de la suma (4) se almacena en la variable a. El resultado de la expresión a++
es el valor que tiene la variable a
antes de que se le almacene el nuevo valor (que era 3), con lo que el resultado de la expresión a++
es 3. Y esto, 3, es lo que se almacena en b
.
En la expresión b = ++a
se evalúa la variable a
(que vale 3), se añade 1 a este valor y el resultado de la suma (4) se almacena en la variable a
. El resultado de la expresión ++a
es el valor que tiene la variable a
después de que se le almacene el nuevo valor (que es 4), con lo que el resultado de la expresión ++a
es 4. Y esto, 4, es lo que se almacena en b
.
Siguiendo estríctamente esta forma de evaluar el incremento pre y postfijo podemos evaluar corréctamente cualquier expresión en que estén involucrados. Por ejemplo :
int a; int b;
a = 3;
b = ++a + a++;
System.out.println( "a="+a + " b=" + b);
a = 3;
b = a++ + a++;
System.out.println( "a="+a + " b=" + b);
En la primera expresión b
acaba valiendo 8 porque el primer ++a
se evalúa a 4 (el valor tras almacenar el incremento) y para cuando se ejecuta el segundo a++
la variable a
ya contiene 4, se incrementa a 5 pero el valor de la expresión a++
es el de la variable antes de incrementarse, con lo que se evalúa a 4. Y 4+4=8.
En la segunda expresión b
acaba valiendo 7 porque el primer a++
se evalua a 3 (el valor antes de almacenar el incremento) y para cuando se ejecuta el segundo a++
la variable a
ya contiene 4, se incrementa a 5 pero el valor de la expresión a++
es el de la variable antes de incrementarse con lo que se evalúa a 4. Y 3+4=7.
Aunque entendamos perféctamente cómo funcionan los operadores de pre y post incremento el utilizarlos en expresiones que no sean triviales solo va a servir para confundir. A ti, a tus cooperadores, a tu coordinador, al que revisa el código, al que tiene que ocuparse del código 5 años después cuando el que lo escribió ya hace tiempo que se fue de la empresa, al gato...