Friday fun: A++, ++A o A+=1? Esa es la cuestión…

Cuando empezamos a programar, una de las cosas que suelen quedar bastante claras (o no) es la diferencia entre i++, ++i e i+=1, mientras que la primera lee y luego asigna, la segunda asigna y luego lee, y la tercera lee y asigna. Personalmente no estaba del todo convencido, así que decidí verlo por mí mismo utilizando el compilador de C#, y comprobar cual era el código IL generado. Las funciones utilizadas son muy simples, y el código se ha compilado en modo debug.

¿Por qué el modo debug? Porque el modo release hace una serie de optimizaciones que dan como resultado exactamente el mismo código IL para un comportamiento sencillo. Veamos la primera función, con su respectivo código IL:

void APlusPlus(int A) 
{
    int B = A++;
}

El código IL es el siguiente:

.method private hidebysig instance void  APlusPlus(int32 A) cil managed
{
  // Code size       9 (0x9)
  .maxstack  3
  .locals init ([0] int32 B)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  dup
  IL_0003:  ldc.i4.1
  IL_0004:  add
  IL_0005:  starg.s    A
  IL_0007:  stloc.0
  IL_0008:  ret
} // end of method Sample::APlusPlus

En este caso el funcionamiento es el siguiente:

  • Se carga el valor de A en la pila
  • Se duplica el valor de A.
  • Se suma 1 al valor de A
  • Se almacena el nuevo valor de A.
  • Se almacena el antiguo valor de A en B (A = B + 1).

Veamos el segundo candidato, en este caso la suma se realiza antes de la lectura.

void PlusPlusA(int A) 
{
    int B = ++A;
}

El código IL se muestra a continuación:

.method private hidebysig instance void  PlusPlusA(int32 A) cil managed
{
  // Code size       9 (0x9)
  .maxstack  2
  .locals init ([0] int32 B)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldc.i4.1
  IL_0003:  add
  IL_0004:  dup
  IL_0005:  starg.s    A
  IL_0007:  stloc.0
  IL_0008:  ret
} // end of method Sample::PlusPlusA

En este caso el funcionamiento es el siguiente:

  • Se carga el valor de A en la pila
  • Se suma 1 al valor de A
  • Se duplica el valor del elemento almacenado en la pila.
  • Se almacena el nuevo valor en A.
  • Se almacena el nuevo valor en B (A = B).

Finalmente, veremos qué pasa con la construcción A+=1;

void APlusOne(int A)
{
    int B = A += 1;
}

El código IL es el siguiente:

.method private hidebysig instance void  APlusOne(int32 A) cil managed
{
  // Code size       9 (0x9)
  .maxstack  2
  .locals init ([0] int32 B)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldc.i4.1
  IL_0003:  add
  IL_0004:  dup
  IL_0005:  starg.s    A
  IL_0007:  stloc.0
  IL_0008:  ret
} // end of method Sample::APlusOne

Si comparamos con el código anterior veremos que el resultado es exactamente igual, sin ningún cambio.

Conclusiones

Estas pequeñas pruebas nos permiten ver cómo funciona internamente el código IL, así como comprobar cómo se manipulan los objetos en operaciones sencillas. Si ejecutamos estas mismas operaciones en modo release cambia por completo, ya que el compilador elimina código no utilizado, así como valores nop que se insertan para facilitar la depuración.

Más info de ildasm en MSDN

Un pensamiento en “Friday fun: A++, ++A o A+=1? Esa es la cuestión…

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s