Tome el siguiente código (utilizable como una aplicación de consola):
static void Main(string[] args)
{
int i = 0;
i += i++;
Console.WriteLine(i);
Console.ReadLine();
}
El resultado i
es 0. Esperaba 2 (como lo hicieron algunos de mis colegas). Probablemente el compilador crea algún tipo de estructura que resulta eni
cero.
La razón por la que esperaba 2 es que, en mi línea de pensamiento, la declaración de la derecha se evaluaría primero, incrementando i con 1. De lo que se agrega a i. Como ya soy 1, agrega 1 a 1. Entonces 1 + 1 = 2. Obviamente, esto no es lo que está sucediendo.
¿Puedes explicar qué hace el compilador o qué sucede en tiempo de ejecución? ¿Por qué el resultado es cero?
Algún tipo de descargo de responsabilidad: estoy absolutamente consciente de que no usará (y probablemente no debería) usar este código. Sé que nunca lo haré. Sin embargo, me parece interesante saber por qué actúa de esa manera y qué está sucediendo exactamente.
i
en el lado izquierdo+=
se "almacena en caché" antes de evaluar el lado derecho. Esto es contrario a la intuición, ya que, por ejemplo, requeriría una operación de copia sii
fuera un objeto. (Por favor, no me entiendan mal: estoy totalmente de acuerdo en afirmar que0
es la respuesta correcta y que cumple con los estándares.)Respuestas:
Esta:
Puede verse como lo hace (lo siguiente es una simplificación excesiva):
Lo que realmente sucede es más complicado que eso: eche un vistazo a MSDN, 7.5.9 operadores de incremento y decremento de postfix :
Tenga en cuenta que debido al orden de precedencia , el postfix
++
ocurre antes+=
, pero el resultado termina sini
usarse (ya que se usa el valor anterior de ).Una descomposición más completa de
i += i++
las partes que lo componen requiere uno para saber que tanto+=
y++
no son atómica (es decir, ninguno de los dos es una sola operación), incluso si se ven como son. La forma en que se implementan implica variables temporales, copias dei
antes de que se realicen las operaciones, una para cada operación. (Usaré los nombresiAdd
yiAssign
para las variables temporales usadas para++
y+=
respectivamente).Entonces, una aproximación más cercana a lo que está sucediendo sería:
fuente
++
operación se realiza antes de que se complete la evaluación de la declaración. Entonces+=
sobrescribe el valor. ¿Es esto lo que pasó?int i = 0; i = i + 1; (postfix) i = 0; (assignment)
. Si usó i en otra parte de esa declaración, se evaluaría a 1 en ese momento.i+1
mientras debería seri=i+1
. ¿No es eso lo quei++
es?Desmontaje del código de ejecución:
Código equivalente
Se compila con el mismo código que el siguiente código:
Desmontaje del segundo código (solo para demostrar que son iguales)
Abrir ventana de desmontaje
La mayoría de las personas no saben, o incluso no recuerdan, que pueden ver el código final de ensamblaje en memoria, usando la ventana Desmontaje de Visual Studio . Muestra el código de máquina que se está ejecutando, no es CIL.
Use esto mientras depura:
Debug (menu) -> Windows (submenu) -> Disassembly
Entonces, ¿qué está pasando con postfix ++?
El postfix ++ dice que nos 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" .
Entonces, ¿qué significa "después de la evaluación" significa:
a = i++ + i
el segundo me afecta el incrementoFunc(i++, i)
el segundo me afecta||
y&&
:(false && i++ != i) || i == 0
el tercero no se ve afectado por i ++ porque no se evalúaEntonces, ¿cuál es el significado de
i += i++;
:?Es lo mismo que
i = i + i++;
El orden de evaluación es:
No es que se descarte el incremento.
¿Cuál es el significado de
i = i++ + i;
:?Esto no es lo mismo que el ejemplo anterior. El tercero
i
se ve afectado por el incremento.El orden de evaluación es:
fuente
a++ + a
no es lo mismo quea + a++
porque esto ya no es pura matemática. La ley de conmutatividad en álgebra no tiene en cuenta la posibilidad de que las variables cambien de valor a la mitad de una expresión. Las matemáticas solo se asignan perfectamente a la programación cuando la programación es funcional. Y ni siquiera entonces, debido a limitaciones de representación. Por ejemplo, los números de coma flotante a veces se comportan como reales y otras no. Incluso sin efectos secundarios, las leyes de conmutatividad y asociatividad que se aplican a los números reales en matemáticas se rompen sobre los números de coma flotante.se evalúa de la siguiente manera:
i
es decir, se cambia dos veces: una por lai++
expresión y otra por la+=
declaración.Pero los operandos de la
+=
declaración soni
antes de la evaluación dei++
(lado izquierdo de+=
) yi
antes de la evaluación dei++
(lado derecho de+=
).fuente
Primero,
i++
devuelve 0. Luegoi
se incrementa en 1. Por último,i
se establece en el valor inicial,i
que es 0 más el valori++
devuelto, que también es cero. 0 + 0 = 0.fuente
i += i++;
,i = i++;
por lo quei++
se agrega el valor de (0)i
y no "i
se establece en el valori++
devuelto". Ahora la pregunta es, cuando se agrega el valori++
devueltoi
, ¿i
será el valor incrementado o el valor no incrementado? La respuesta, mi amigo, está escrita en las especificaciones.i = 0
principio,i += something
es equivalente a loi = 0 + something
que esi = something
.Esto es simplemente de izquierda a derecha, evaluación ascendente del árbol de sintaxis abstracta. Conceptualmente, el árbol de la expresión se camina de arriba hacia abajo, pero la evaluación se desarrolla a medida que la recursión vuelve a subir al árbol desde la parte inferior.
La evaluación comienza considerando el nodo raíz
+=
. Ese es el componente principal de la expresión. El operando izquierdo de+=
debe evaluarse para determinar el lugar donde almacenamos la variable y obtener el valor anterior que es cero. A continuación, se debe evaluar el lado derecho.El lado derecho es un
++
operador de incremento posterior . Tiene un operando,i
que se evalúa como una fuente de un valor y como un lugar donde se debe almacenar un valor. El operador evalúai
, encuentra0
y, en consecuencia, almacena un1
en esa ubicación. Devuelve el valor anterior,0
, de acuerdo con su semántica de devolver el valor anterior.Ahora el control vuelve al
+=
operador. Ahora tiene toda la información para completar su operación. Conoce el lugar donde almacenar el resultado (la ubicación de almacenamiento dei
), así como el valor anterior, y tiene el valor agregado al valor anterior, a saber0
. Entonces,i
termina con cero.Al igual que Java, C # ha desinfectado un aspecto muy estúpido del lenguaje C al fijar el orden de evaluación. De izquierda a derecha, de abajo hacia arriba: el orden más obvio que los codificadores pueden esperar.
fuente
SetSum(ref i, Inc(ref i))
conint SetSum(ref int a, int b) { return a += b; }
yint Inc(ref int a) { return a++; }
... por supuesto, ya no espero eso.Set(ref i, Sum(i, Inc(ref i)))
conint Set(ref int a, int b) { return a = b; }
yint Sum(int a, int b) { return a + b; }
.SetSum
es que no evalúa el operando izquierdoi
, sino que solo toma su dirección, por lo que no es equivalente a una evaluación completa de izquierda a derecha del operando. Necesitas algo como esoSetSum(ref i, i, PostInc(ref i))
. El segundo argumento deSetSum
es el valor a agregar, donde solo usamosi
para especificar el valor anterior dei
.SetSum
es justoint SetSum(ref int dest, int a, int b) { return dest = a + b; }
.a += b
ena = a + b
... mostrando que el operador + = no es atómico ... es solo azúcar sintáctico.Porque
i++
primero devuelve el valor, luego lo incrementa. Pero después de que se establece en 1, lo vuelve a establecer en 0.fuente
El método de incremento posterior se parece a esto
Entonces, básicamente, cuando llamas
i++
,i
es incremental, pero el valor original se devuelve en tu caso, se devuelve 0.fuente
Respuesta simple
fuente
i ++ significa: devolver el valor de i ENTONCES incrementarlo.
i + = i ++ significa: Tome el valor actual de i. Agregue el resultado de i ++.
Ahora, agreguemos i = 0 como condición inicial. i + = i ++ ahora se evalúa así:
Nota: Al final del paso 2, el valor de i es en realidad 1. Sin embargo, en el paso 3, lo descarta cargando el valor de i antes de que se incremente.
A diferencia de i ++, ++ i devuelve el valor incrementado.
Por lo tanto, i + = ++ te daría 1.
fuente
El operador de incremento posterior al arreglo
++
, le da a la variable un valor en la expresión y luego realiza el incremento que asignó, devolvió el valor cero (0)i
nuevamente que sobrescribe el incremento (1) , por lo que está obteniendo cero. Puede leer más sobre el operador de incremento en ++ Operador (MSDN).fuente
i += i++;
será igual a cero, porque lo hace++
después.i += ++i;
lo haré antesfuente
++
después, esperaría que el resultado sea1
.El postfix ++ se evalúa
i
antes de incrementarlo, y+=
solo se evalúai
una vez.Por lo tanto, 0 + 0 = 0, como
i
se evalúa y usa antes de que se incremente, ya que++
se usa el formato de postfix de . Para aumentari
primero, use el formulario de prefijo (++i
).(Además, solo una nota: solo debe obtener 1, ya que 0 + (0 + 1) = 1)
Referencias: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)
fuente
Qué está haciendo C # y el "por qué" de la confusión
También esperaba que el valor fuera 1 ... pero algunas exploraciones al respecto aclararon algunos puntos.
Cosider los siguientes métodos:
Esperaba que
i += i++
fuera lo mismo queSetSum(ref i, Inc(ref i))
. El valor de i después de esta declaración es 1 :Pero luego llegué a otra conclusión ... en
i += i++
realidad es lo mismo quei = i + i++
... así que he creado otro ejemplo similar, usando estas funciones:Después de llamar a esto,
Set(ref i, Sum(i, Inc(ref i)))
el valor de i es 0 :Esto no solo explica lo que C # está haciendo ... sino también por qué mucha gente se confundió con él ... incluyéndome a mí.
fuente
Una buena mnemónica que siempre recuerdo sobre esto es la siguiente:
Si
++
está detrás de la expresión, devuelve el valor que era antes . Entonces el siguiente códigoes 1, porque
a
era 1 antes de que fuera incrementado por la++
situación posteriora
. La gente llama a este mensaje notación solución. También hay una notación previa , donde las cosas son exactamente lo opuesto: si++
está antes , la expresión devuelve el valor que está después de la operación:b
Hay dos aquí.Entonces, para su código, esto significa
i++
devuelve 0 (como se describió anteriormente), entonces0 + 0 = 0
.Scott Meyers describe la diferencia entre esas dos notaciones en "Programación efectiva de C ++". Internamente,
i++
(postfix) recuerda el valori
era, y llama al prefijo de notación (++i
) y devuelve el valor antiguo,i
. Es por esto que se debe utilizar Allways++i
enfor
bucles (aunque creo que todos los compiladores modernos están traduciendoi++
a++i
enfor
bucles).fuente
int i = 0; i += (++i)
yi
está configurado en uno en lugar de dos. Tiene sentido para mí también, ya que el uso del prefijo en lugar de la de sufijo no cambia el hecho de que, si se escribei += (++i)
ai = i + (++i)
, eli
resultado se calcula antes++i
, dando como resultadoi = 0 + (++i)
en última instanciai = 0 + 1
.La única respuesta correcta a su pregunta es: porque no está definida.
Ok, antes de que todos me quemen ...
Todos respondieron por qué
i+=i++
está bien y es lógico dar como resultadoi=0
.Tuve la tentación de rechazar cada una de sus respuestas, pero la reputación que calculé sería demasiado alta.
¿Por qué estoy tan enojado con ustedes? no por lo que explican sus respuestas ...
Quiero decir, cada respuesta que leí había hecho un esfuerzo notable para explicar lo imposible, ¡Aplausos!
¿Pero cuál es el resultado? ¿Es un resultado intuitivo? ¿Es un resultado aceptable?
Cada uno de ustedes vio al "rey desnudo" y de alguna manera lo aceptó como un rey racional.
¡Todos ustedes están EQUIVOCADOS!
i+=i++;
El resultado0
es indefinido.un error en el mecanismo de evaluación del lenguaje si lo desea ... ¡o incluso peor! Un error en el diseño.
quieres una prueba? por supuesto que quieres!
int t=0; int i=0; t+=i++; //t=0; i=1
Ahora esto ... ¡es un resultado intuitivo! porque primero evaluamos lo
t
asignamos con un valor y solo después de la evaluación y la asignación tuvimos la operación posterior, racional, ¿no?¿es racional que:
i=i++
yi=i
produzca el mismo resultadoi
?mientras
t=i++
yt=i
tienen diferentes resultados parai
.La operación posterior es algo que debería ocurrir después de la evaluación de la declaración.
Por lo tanto:
Debería ser lo mismo si escribimos:
y por lo tanto lo mismo que:
y por lo tanto lo mismo que:
Cualquier resultado que no
1
indique un error en el cumplidor o un error en el diseño del lenguaje si vamos con un pensamiento racional, sin embargo, MSDN y muchas otras fuentes nos dicen "¡Oye, esto no está definido!"Ahora, antes de continuar, incluso este conjunto de ejemplos que di no es compatible ni reconocido por nadie. Sin embargo, esto es lo que de acuerdo con la forma intuitiva y racional debería haber sido el resultado.
¡El codificador no debe saber cómo se está escribiendo o traduciendo la asamblea!
Si está escrito de una manera que no respetará las definiciones del lenguaje, ¡es un error!
Y para terminar, copié esto de Wikipedia, operadores de incremento y decremento :
dado que el operador de incremento / decremento modifica su operando, el uso de dicho operando más de una vez dentro de la misma expresión puede producir resultados indefinidos . Por ejemplo, en expresiones como x - ++ x, no está claro en qué secuencia se deben realizar los operadores de resta e incremento. Situaciones como esta empeoran aún más cuando el compilador aplica optimizaciones, lo que podría resultar en que el orden de ejecución de las operaciones sea diferente de lo que pretendía el programador.
Y por lo tanto.
¡La respuesta correcta es que esto NO DEBE SER USADO! (ya que no está definido!)
Sí ... - Tiene resultados impredecibles incluso si el cumplidor de C # está intentando normalizarlo de alguna manera.
No encontré ninguna documentación de C # que describa el comportamiento que todos ustedes documentaron como un comportamiento normal o bien definido del lenguaje. ¡Lo que encontré es exactamente lo contrario!
[ copiado de la documentación de MSDN para operadores de incremento y disminución de Postfix: ++ y - ]
Cuando se aplica un operador postfix a un argumento de función, no se garantiza que el valor del argumento se incremente o disminuya antes de pasarlo a la función. Consulte la sección 1.9.17 en el estándar C ++ para obtener más información.
Note esas palabras no garantizadas ...
Perdóname si esa respuesta parece arrogante: no soy una persona arrogante. Solo considero que miles de personas vienen aquí para aprender y las respuestas que leo los engañarán y dañarán su lógica y comprensión del tema.
fuente
i=++i
proporcionará resultados diferentes dei=i++
. Por lo tanto, mi respuesta se mantiene.El operador ++ después de la variable la convierte en un incremento de postfix. El incremento ocurre después de todo lo demás en la declaración, la suma y la asignación. Si, en cambio, coloca el ++ antes de la variable, sucederá antes de que se evalúe el valor de i, y le dará la respuesta esperada.
fuente
++
no sucede después de la+=
declaración, sucede durante la ejecución de la+=
declaración. Es por eso que los efectos de++
obtener anulación por el+=
.+=
sobrescribe la modificación debido al incremento previo o posterior en la expresión.Los pasos en el cálculo son:
int i=0
// Inicializado a 0i+=i++
//Ecuacióni=i+i++
// después de simplificar la ecuación por compiladori=0+i++
// i valor de sustitucióni=0+0
// i ++ es 0 como se explica a continuacióni=0
// Resultado final i = 0Aquí, inicialmente el valor de
i
es 0. WKT,i++
no es más que: primero use eli
valor y luego incremente eli
valor en 1. Entonces usa eli
valor, 0, mientras calculai++
y luego lo incrementa en 1. Entonces resulta en un valor de 0.fuente
Hay dos opciones:
La primera opción: si el compilador lee la declaración de la siguiente manera,
entonces el resultado es 2.
por
El resultado es 1.
fuente
Tenga mucho cuidado: lea las preguntas frecuentes de C : lo que está tratando de hacer (mezcla de asignación y
++
de la misma variable) no solo no está especificado, sino que también está indefinido (lo que significa que el compilador puede hacer cualquier cosa al evaluar, no solo dar resultados "razonables").Por favor lea la sección 3 . ¡Vale la pena leer toda la sección! Especialmente 3.9, lo que explica la implicación de no especificado. La Sección 3.3 le ofrece un resumen rápido de lo que puede y no puede hacer con "i ++" y similares.
Dependiendo de los compiladores internos, puede obtener 0, o 2, o 1, ¡o incluso cualquier otra cosa! Y como no está definido, está bien que lo hagan.
fuente
Hay mucho razonamiento excelente en las respuestas anteriores, acabo de hacer una pequeña prueba y quiero compartir con ustedes
Aquí el resultado i muestra 0 resultado. Ahora considere los siguientes casos:
Caso 1:
anteriormente pensé que el código anterior se parece a esto, así que a primera vista la respuesta es 1, y realmente la respuesta de i para este es 1.
Caso 2:
aquí el operador de incremento no viene en la ruta de ejecución, a diferencia del caso anterior donde i ++ tiene la posibilidad de ejecutarse antes de la adición.
Espero que esto ayude un poco. Gracias
fuente
Con la esperanza de responder a esto desde una perspectiva de programación C 101.
Me parece que está sucediendo en este orden:
i
se evalúa como 0, lo que resulta eni = 0 + 0
la operación de incrementoi++
"en cola", pero la asignación de 0 ai
aún no ha sucedido.i++
ocurrei = 0
de arriba sucede, sobrescribiendo efectivamente todo lo que el # 2 (el post-incremento) hubiera hecho.Ahora, el # 2 puede que nunca suceda (¿probablemente no?) Porque el compilador probablemente se dé cuenta de que no servirá para nada, pero esto podría depender del compilador. De cualquier manera, otras respuestas más informadas han demostrado que el resultado es correcto y se ajusta al estándar C #, pero no está definido qué sucede aquí para C / C ++.
Cómo y por qué está más allá de mi experiencia, pero el hecho de que la asignación del lado derecho evaluada previamente ocurra después del incremento posterior es probablemente lo que es confuso aquí.
Además, no esperaría que el resultado fuera 2, a menos que lo hiciera en
++i
lugar de loi++
que creo.fuente
2
C ++: ideone.com/8dH8tfSimplemente pon,
i ++, agregará 1 a "i" después de que el operador "+ =" se haya completado.
Lo que desea es ++ i, para que agregue 1 a "i" antes de que se ejecute el operador "+ =".
fuente
Luego se agrega el 1 a
i
.i + = i ++
Entonces, antes de agregar 1 a
i
,i
tomó el valor de 0. Solo si agregamos 1 antes,i
obtenga el valor 0.fuente
La respuesta es
i
será1
.Veamos cómo:
Inicialmente
i=0;
.Luego, mientras calculamos de
i +=i++;
acuerdo con el valor de, tendremos algo así0 +=0++;
, de acuerdo con la precedencia del operador,0+=0
se realizará primero y el resultado será0
.Luego, el operador de incremento se aplicará como
0++
, como0+1
y el valor dei
será1
.fuente
0 += 0++;
asignación es después del incremento++
pero con el valor de i interpretado antes de++
(porque es un operador de publicación.)