domingo, 8 de mayo de 2011

Java: Operadores Incremento/Decremento

Los operadores de pre/post-incremento/decremento pueden ser fuente de confusión a pesar de lo simples que parecen. ¿Qué imprime el siguiente fragmento de código?

  int i = 10, k = 10, x;
  x = (i++*--k) + (i++*--k);
  System.out.println("x=" + x + ",i=" + i + ",k=" + k); 

Hasta donde entendía, los operadores de pre-incremento/decremento se ejecutan antes de evaluar la expresión completa y los de post-incremento/decremento después de evaluar la expresión completa. En nuestro ejemplo, yo pensé que los "--k" eran ejecutados primero, dejando k=8. Después se ejecutaban las operaciones, x = (10*8) + (10*8) = 160. Y por último incrementamos dos veces i, es decir i=12. Para mi sorpresa, el resultado fue: x=178,i=12,k=8.

Esto despertó mi curiosidad de ver lo que realmente estaba ejecutando la máquina virtual de java. Para ello, utilicé el comando javap para obtener una versión más amigable del bytecode que se encuentra en los archivos .class. La siguiente tabla muestra del lado izquierdo la salida de javap y del lado derecho una versión más fácil de leer.

Pasojavap 
1bipush 10
istore_0
i=10
2bipush 10
istore_1
k=10
3iload_0i_temp = i = 10
4iinc 0, 1i=11
5iinc 1, -1k=9
6iload_1k_temp = k = 9
7imulsumando_1 = i_temp * k_temp = 90
8iload_0i_temp = i = 11
9iinc 0, 1i = 12
10iinc 1, -1k = 8
11iload_1k_temp = k = 8
12imulsumando_2 = i_temp * k_temp = 88
13iaddsuma = sumando_1 + sumando_2 = 178
14istore_2x = suma = 178

Observen en el paso 7, que el primer sumando es calculado usando variables temporales (siendo más precisos, es un stack de operandos que usa la JVM). De hecho, en los pasos 3 al 6 se guardan los valores de i y k en el stack de operandos y también se realiza su respectiva operación de post-incremento y pre-decremento. La diferencia es que para i, primero se copia su valor en el stack de operandos y luego se incrementa; y para k, primero se decrementa su valor y después se copia en el stack.

Al momento de ejecutar el paso 7, las dos variables i(11) y k(9) ya fueron modificadas. Los pasos 8-12 son similares a los pasos 3-7. Por último, en los pasos 13-14 se calcula y se guarda el resultado final. Es importante notar que las operaciones de pre/post-incremento/decremento afectan a su operando conforme van siendo evaluadas dentro de una expresión más compleja. La única diferencia entre pre y post es si el valor del operando se copia al stack de operandos antes o depués de ser modificado. Sin embargo, subsequentes ocurrencias de la misma variable dentro de la expresión verán el valor modificado (por ejemplo, la segunda ocurrencia de i o k en la expresión anterior.)

Como ejercicio, ¿cuál es el valor de r al ejecutar la siguiente operación?

  int x = 10;
  int r = ++x * x++ * x

Es claro que los operadores de pre/post-incremento/decremento pueden ser fuente de confusión, tal vez esa es una razón por la cual el lenguaje Python no contiene estos operadores. ¿Qué opinan? ¿Qué resultado obtuvieron en el ejercicio anterior?

1 comentario:

  1. chvr..me salio 1452..esta bien?...publica mas ejercicios....los mas trancas que conoscas

    ResponderEliminar