252 votos

Para i = 0, ¿por qué es (i += i++) igual a 0?

Tome el código siguiente (se puede usar como una Aplicación de Consola):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

El resultado de i es 0. Yo esperaba 2 (como algunos de mis compañeros). Probablemente el compilador crea algún tipo de estructura que los resultados en i son cero.

La razón por la que yo esperaba 2 es que, en mi línea de pensamiento, la mano derecha de la declaración, que será evaluada en primer lugar, incrementar i en 1. Que se agrega a yo. Desde que ya es el 1, es la adición de 1 a 1. Así que 1 + 1 = 2. Obviamente esto no es lo que está sucediendo.

Puede usted explicar lo que el compilador hace o sucede en tiempo de ejecución? ¿Por qué el resultado de cero?

Algunos-tipo-de-descargo de responsabilidad: soy absolutamente consciente de que no (y probablemente no debería) utilice este código. Sé que nunca lo haré. Sin embargo, creo que es interesante saber por qué actúa de tal manera y lo que está ocurriendo exactamente.

425voto

Oded Puntos 271275

Este:

int i = 0;
i += i++

Puede ser visto como usted que hace (la siguiente es una burda simplificación):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Lo que en realidad sucede es más complicado que eso - echa un vistazo a MSDN, 7.5.9 Postfix operadores de incremento / decremento:

El tiempo de ejecución de la tramitación de una postfix operación de incremento o decremento de la forma x++ x-- consta de los siguientes pasos:

  • Si x es clasificado como una variable:

    • x se evalúa para producir la variable.
    • El valor de x se guarda.
    • El operador seleccionado se invoca con el valor guardado de x como argumento.
    • El valor devuelto por el operador se almacena en la ubicación dada por la evaluación de x.
    • El guarda el valor de x se convierte en el resultado de la operación.

Tenga en cuenta que debido a la orden de precedencia, el postfix ++ ocurre antes de la +=, pero el resultado termina siendo no utilizados (como el anterior valor de i ).


De una manera más profunda descomposición de i += i++ a los componentes de los que está hecha, se requiere que tanto conocemos += y ++ no son atómico (es decir, ninguno de los dos es de una sola operación), incluso si parecen que son. La forma en que estos se implementan involucran variables temporales, copias de i antes de que se realicen las operaciones, una para cada operación. (Voy a usar los nombres iAdd y iAssign para las variables temporales utilizados para ++ y += respectivamente).

Así, una aproximación más cercana a lo que está ocurriendo sería:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

194voto

Miguel Angelo Puntos 12471

Desmontaje de la ejecución de código:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Código equivalente

Se compila en el mismo código como el siguiente código:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Desmontaje de la segunda de código (sólo para demostrar que son el mismo)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Apertura desmontaje de ventana

La mayoría de las personas no saben, o no recuerdan, que pueden ver al final de la memoria de código ensamblador, utilizando Visual Studio Desmontaje de la ventana. Muestra el código de máquina que se ejecuta, no es CIL.

El uso de este mientras depuración:

Debug (menu) -> Windows (submenu) -> Disassembly

Entonces, ¿qué está sucediendo con postfix++?

El postfix++ dice que le gustaría incrementar el valor del operando después de la evaluación... que todo el mundo sabe... lo que confunde un poco es el significado de "después de la evaluación".

Así que ¿qué significa "después de la evaluación" significa:

  • otros usos del operando, en la misma línea de código debe ser afectado:
    • a = i++ + i el segundo que se ve afectado por el incremento de
    • Func(i++, i) el segundo me está afectada
  • otros usos en la misma línea respecto de corto circuito del operador como || y &&:
    • (false && i++ != i) || i == 0 la tercera no es afectado por i++ ya que no se evalúa

Entonces, ¿cuál es el significado de: i += i++;?

Es lo mismo que i = i + i++;

El orden de evaluación es:

  1. Tienda i + i (que es 0 + 0)
  2. Incremento de la i se convierte en 1)
  3. Asignar el valor del paso 1 para i (i es 0)

No es que el incremento es de desecharse.

¿Cuál es el significado de: i = i++ + i;?

Este no es el mismo que en el ejemplo anterior. La 3ª i está afectado por el incremento.

El orden de evaluación es:

  1. Tienda yo (que es 0)
  2. Incremento de la i se convierte en 1)
  3. Almacenar el valor del paso 1 + i (0 + 1)
  4. Asignar el valor de paso de 3 a i i se convierte en 1)

61voto

dtb Puntos 104373
int i = 0;
i += i++;

se evalúa de la siguiente manera:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

es decir, i se ha cambiado dos veces: una por el i++ expresión y una vez por la += declaración.

Pero los operandos de la += estado de

  • el valor i antes de la evaluación de i++ (en la parte izquierda de +=) y
  • el valor i antes de la evaluación de i++ (lado derecho de +=).

36voto

Jong Puntos 6452

En primer lugar, i++ devuelve 0. A continuación, i se incrementa en 1. Por último i se establece en el valor inicial de i 0, más el valor i++ devuelve, que es cero. 0 + 0 = 0.

32voto

Kaz Puntos 18072

Esto es simplemente de izquierda a derecha, de abajo hacia arriba de la evaluación del árbol de sintaxis abstracta. Conceptualmente, la expresión del árbol es caminó de arriba hacia abajo, pero la evaluación se desarrolla a medida que la recursividad emerge de nuevo el árbol de la parte inferior.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

La evaluación comienza por examinar el nodo raíz +=. Que es el constituyente principal de la expresión. El operando de la izquierda += debe ser evaluado para determinar el lugar donde se almacena la variable, y obtener previamente el valor de cero. Luego, el lado derecho debe ser evaluado.

El lado derecho es un post de incremento ++ operador. Tiene un operando, i que se evalúa como una fuente de valor, y como un lugar donde el valor es el que se almacenan. El operador evalúa i, encontrando 0, y, en consecuencia, almacena 1 en ese lugar. Devuelve el valor previo, 0, de acuerdo con su semántica de devolver el valor previo.

Ahora el control es posterior a la += operador. Ahora tiene toda la información para completar su operación. Se conoce el lugar donde se almacena el resultado (la ubicación de almacenamiento de i) así como el valor previo, y tiene el valor agregado al valor previo, a saber, 0. Así, i termina con cero.

Como Java, C# ha saneado un muy estúpida de aspecto de el lenguaje C fijando el orden de evaluación. De izquierda a derecha, de abajo hacia arriba: el más evidente el fin de que pueda ser esperado por los programadores.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X