¿Por qué esto (i = ++ i% 3) genera una advertencia: “puede estar indefinido”?

int main(void) { int i = 0; i = ++i % 3; return 0; } 

Lo compilo así:

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

¿Por qué el comstackdor dice que puedo estar indefinido?

Como han señalado otros, el comportamiento no está definido :

6.5 Expresiones

2 Entre el punto de secuencia anterior y el siguiente, un objeto tendrá su valor almacenado modificado a lo sumo 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 punto flotante no es un objeto y se puede establecer más de una vez dentro de una expresión. 73) Este párrafo representa expresiones de statement no definidas como

     i = ++ i + 1;
     a [i ++] = i;

mientras que permite

     i = i + 1;
     a [i] = i;

La expresión i = ++i % 3 intenta modificar el valor contenido en i dos veces antes del siguiente punto de secuencia (en este caso, el ; finalizando la instrucció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 de una expresión se evalúen en ningún orden en particular, o que los efectos secundarios de una expresión se apliquen inmediatamente después de evaluar la expresión (a diferencia de C # y Java, que hacer esas garantías). Por ejemplo, la expresión ++i tiene un resultado ( i + 1) y un efecto secundario (incrementa el valor almacenado en i ); sin embargo, el efecto secundario se puede diferir 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 permita a los comstackdores reordenar las evaluaciones de una manera óptima (aprovechando un valor que ya está en un registro, por ejemplo). El inconveniente es que ciertas combinaciones de expresiones tendrán resultados impredecibles.

Porque estás modificando el valor de i dos veces sin un punto de secuencia intermedio. Es un comportamiento indefinido .

En la norma, 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 punto real es: ¿por qué demonios alguien escribiría tal código?

¿Cuál es el valor que quieres que tenga? Si está asignando un valor completamente nuevo a 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 tuviera un significado, entonces, en el mejor de los casos, el incremento i se reemplaza inmediatamente con el nuevo valor asignado. Entonces, ¿por qué no escribirlo como

 i = (i+1) % 3; 

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