2011-10-24 9 views
5
int main(void) 
{ 
    int i = 0; 
    i = ++i % 3; 
    return 0; 
} 

puedo compilar esta manera:¿Por qué esto (i = ++ i% 3) genera una advertencia: "puede estar indefinido"?

$ gcc -Wall main.c -o main 
main.c: In function ‘main’: 
main.c:4: warning: operation on ‘i’ may be undefined 

¿Por qué decimos que el compilador i puede ser indefinido?

+2

La * operación * no está definida, no es 'i' en sí misma. Es decir. es un comportamiento indefinido. No lo hagas –

+0

@Paul R ¿por qué crees que la operación no está definida? ++ i aumenta i a 1 y el módulo opeartor 1 con 3 da 1 como resultado. no funciona? – niko

+1

@niko: Vea mi respuesta y http://www.catb.org/jargon/html/N/nasal-demons.html –

Respuesta

4

Como otros han señalado, el comportamiento es undefined:

6.5 Expresiones
...
2 entre el anterior y el siguiente punto de la secuencia de un objeto tendrá su valor almacenado modificado como máximo una vez por la evaluación de una expresión. 72) Además, el valor anterior se leerá solo para determinar el valor que se almacenará. 73)
...
72) Un indicador de estado de coma flotante no es un objeto y puede configurarse más de una vez dentro de una expresión. 73) Este párrafo renders expresiones de los estados no definidos tales como
 
    i = ++i + 1; 
    a[i++] = i; 
al tiempo que permite
 
    i = i + 1; 
    a[i] = i; 

La expresión i = ++i % 3 intentos de modificar el valor contenido en idos veces antes de que el siguiente punto de secuencia (en este caso, el ; finalizando la declaración), una vez evaluando ++i, y una vez evaluando la expresión de asignación más grande.

Ahora, ¿por qué sería esto un problema? Después de todo, C# y Java pueden manejar estas expresiones muy bien. El problema es que, con pocas excepciones, C no garantiza que los operandos en una expresión se evalúen en un orden particular, o que los efectos secundarios de una expresión se apliquen inmediatamente después de que se evalúe la expresión (a diferencia de C# y Java, que hacen esas garantías).Por ejemplo, la expresión ++i tiene un resultado (i + 1) y un efecto lado (incrementar el valor almacenado en i); sin embargo, el efecto secundario puede diferirse hasta que se haya evaluado la expresión más grande. IOW, se permite la siguiente secuencia de acciones:

 
    t0 = i + 1 
    t1 = t0 % 3 
    i = t1 
    i = i + 1 

Oopsie. No es lo que queríamos

Esta fue una decisión de diseño deliberada; la idea es que permite a los compiladores reordenar las evaluaciones de manera óptima (por ejemplo, aprovechando un valor que ya está en un registro). La desventaja es que ciertas combinaciones de expresiones tendrán resultados impredecibles.

6

En standardese, es un comportamiento indefinido porque i se modifica dos veces sin un punto de secuencia intermedio.

i = ++i % 3; 

Pero ese no es realmente el punto. El verdadero punto es: ¿Por qué demonios alguien escribiría tal código?

¿Cuál es el valor que desea que tenga i? Si está asignando un valor completamente nuevo en i con i = ..., ¿qué efecto está tratando de lograr con ++i? Si esto fuera paralelo-universo-C en el que un código como este realmente tenía significado, entonces, en el mejor de los casos, el i incrementado se reemplaza inmediatamente con el nuevo valor total asignado. Entonces, ¿por qué no simplemente escribirlo como

i = (i+1) % 3; 

que también es correcto en C-tal como lo conocemos.

+1

+1 por "¿por qué demonios alguien escribiría ese código?" –

+0

+1 por "¿por qué demonios alguien escribiría tal código?" :) –

Cuestiones relacionadas