Pregunta

¿Puede alguien explicarme por qué este código imprime 14? Otro estudiante me preguntó y no pude resolverlo.

int i = 5;
i = ++i + ++i;
cout<<i;
¿Fue útil?

Solución

El orden de los efectos secundarios no está definido en C ++. Además, la modificación de una variable dos veces en una sola expresión no tiene un comportamiento definido (Ver estándar C ++ , & # 167; 5.0.4, página física 87 / página lógica 73).

Solución: no use efectos secundarios en expresiones complejas, no use más de uno en expresiones simples. Y no está de más habilitar todas las advertencias que el compilador puede darle: Agregar -Wall (gcc) o /Wall /W4 (Visual C ++) a la línea de comando genera una advertencia adecuada:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Obviamente, el código se compila para:

i = i + 1;
i = i + 1;
i = i + i;

Otros consejos

Ese es un comportamiento indefinido, el resultado variará dependiendo del compilador que use. Consulte, por ejemplo, C ++ FAQ Lite .

En algunas de las respuestas / comentarios ha habido una discusión sobre el significado de "comportamiento indefinido" y si eso invalida el programa. Así que publico esta respuesta bastante larga que detalla exactamente lo que dice el estándar con algunas notas. Espero que no sea demasiado aburrido ...

Los bits citados del estándar provienen del estándar C ++ actual (ISO / IEC 14882: 2003). Hay una redacción similar en el estándar C.

Según el estándar C ++, modificar un valor más de una vez dentro de un conjunto de puntos de secuencia da como resultado un comportamiento indefinido (sección 5, párrafo 4):

  

Excepto donde se indique, el orden de   evaluación de operandos de individuo   operadores y subexpresiones de   expresiones individuales y el orden   en el que se producen los efectos secundarios, es   sin especificar.53) Entre las anteriores   y la siguiente secuencia apunta un escalar   objeto tendrá su valor almacenado   modificado a lo más una vez por el   evaluación de una expresión.   Además, el valor anterior será   accedido solo para determinar el valor   para ser almacenado. Los requisitos de esto   el párrafo se cumplirá para cada   orden permisible de la   subexpresiones de una expresión completa;   de lo contrario, el comportamiento es indefinido.   [Ejemplo:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
     

& # 8212; final del ejemplo]

Tenga en cuenta que el segundo ejemplo, " i = 7, i++, i++; " se define ya que el operador de coma es un punto de secuencia.

Esto es lo que el estándar de C ++ dice que significa "comportamiento indefinido":

  

1.3.12 comportamiento indefinido [defns.undefined]

     

comportamiento, tal como podría surgir con el uso de una construcción de programa errónea o datos erróneos, para lo cual esto   Norma Internacional no impone requisitos. También se puede esperar un comportamiento indefinido cuando esto   Norma Internacional omite la descripción de cualquier definición explícita de comportamiento. [Nota: permisible indefinido   el comportamiento varía desde ignorar la situación por completo con resultados impredecibles, hasta comportarse durante   traducción o ejecución del programa de manera documentada característica del entorno (con o sin   la emisión de un mensaje de diagnóstico), a la terminación de una traducción o ejecución (con la emisión de un   mensaje de diagnóstico). Muchas construcciones de programa erróneas no generan un comportamiento indefinido; están obligados a ser diagnosticados. ]

En otras palabras, el compilador es libre de hacer lo que quiera, incluyendo

  1. escupiendo un mensaje de error,
  2. haciendo algo implementación definida & amp; documentado,
  3. tener resultados completamente impredecibles

El segundo elemento cubre las extensiones de idioma que tienen la mayoría de los compiladores, pero, por supuesto, no están definidas en el estándar.

Así que supongo que, estrictamente hablando, algo que exhibe un comportamiento indefinido no es 'ilegal', pero en mi experiencia siempre que ha habido algo en un programa C / C ++ que exhibe un 'comportamiento indefinido' (a menos que sea una extensión), es un error . Creo que llamar ilegal a tal construcción no es confuso, engañoso o equivocado.

Además, creo que tratar de explicar lo que está haciendo el compilador para alcanzar el valor 14 no es particularmente útil, ya que eso pierde el sentido. El compilador podría estar haciendo casi cualquier cosa; de hecho, es probable que el compilador pueda alcanzar un resultado diferente cuando se ejecuta utilizando diferentes opciones de optimización (o puede producir código que falla, ¿quién sabe?).

Para aquellos que desean algunas referencias adicionales o una apelación a la autoridad, aquí hay algunos consejos:

Respuesta larga y larga de Steve Summit (responsable de las preguntas frecuentes comp.lang.c) sobre este tema desde 1995:

Esto es lo que Bjarne Stroustrup tiene que decir al respecto:


Nota al pie : el estándar C ++ usa la palabra 'ilegal' exactamente una vez, cuando describe una diferencia entre C ++ y el Estándar C con respecto al uso de static o extern con declaraciones de tipo.

Simple ... su compilador está evaluando AMBOS incrementos antes de realizar la suma, sin almacenar en caché los resultados intermedios. Esto significa que cuando agrega i dos veces, ahora tiene el valor de 7.

Si lo haces

int j=++i; 
int k=++i;

i = j+k;

verá 13 como se esperaba.

En su compilador particular, está eligiendo hacer las dos operaciones ++ primero, luego la adición. Está interpretando el código como:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

Eso es perfectamente válido.

Una mejor pregunta sería, ¿siempre será 14?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

Con toda probabilidad, probablemente sea <=>, porque tiene un poco más sentido.

Creo que al mirar el problema desde la vista del árbol de sintaxis, la respuesta al problema se vuelve más clara:

i
|
=
|
+
|
expresión unaria - expresión unaria

expresión unaria: expresión de operador unario

En nuestro caso, la expresión se reduce a la variable i.

Ahora, lo que sucede es que ambas expresiones unarias modifican el mismo operando, por lo que el código hace dos veces ++ i al evaluar las expresiones unarias antes de agregar los resultados de ambas expresiones unitarias.

Entonces, lo que hace el código es de hecho

++ i;
++ i;
i = i + i;

Para i = 5 eso significa

i = i + 1; // i < - 6
i = i + 1; // i < - 7
i = i + i; // i < - 14

Porque el incremento del prefijo tiene prioridad:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top