¿Error de comstackción de C ++?

Tengo el siguiente código:

#include  #include  using namespace std; int main() { complex delta; complex mc[4] = {0}; for(int di = 0; di < 4; di++, delta = mc[di]) { cout << di << endl; } return 0; } 

Espero que muestre “0, 1, 2, 3” y pare, pero produce una serie interminable de “0, 1, 2, 3, 4, 5, …..”

Parece que la comparación di<4 no funciona bien y siempre devuelve verdadero.

Si acabo de comentar ,delta=mc[di] , obtengo “0, 1, 2, 3” como es normal. ¿Cuál es el problema con la asignación inocente?

Estoy usando Ideone.com g ++ C ++ 14 con la opción -O2.

Esto se debe a un comportamiento indefinido, está accediendo a la matriz mc fuera de límites en la última iteración de su ciclo. Algunos comstackdores pueden realizar una optimización agresiva de bucle en torno a las suposiciones de un comportamiento no definido. La lógica sería similar a la siguiente:

  • Acceder a mc fuera de límites es un comportamiento indefinido
  • Asumir un comportamiento indefinido
  • Por lo tanto, di < 4 es siempre cierto ya que de lo contrario mc[di] invocaría un comportamiento indefinido

gcc con la optimización activada y el uso del -fno-aggressive-loop-optimizations provoca que el comportamiento del bucle infinito desaparezca ( -fno-aggressive-loop-optimizations en vivo ). Mientras que un ejemplo en vivo con optimización pero sin optimizaciones de bucle no agresivo muestra el comportamiento de bucle infinito que observa.

Un ejemplo de Godbolt Live del código muestra que la verificación di < 4 se elimina y se reemplaza con jmp incondicional:

 jmp .L6 

Esto es casi idéntico al caso delineado en GCC pre-4.8 Breaks Broken SPEC 2006 Benchmarks . Los comentarios a este artículo son excelentes y bien vale la pena leerlos. Observa que clang captó el caso en el artículo usando -fsanitize=undefined que no puedo reproducir para este caso, pero gcc usa -fsanitize=undefined does ( see it live ). Probablemente el error más infame en torno a un optimizador que hace una inferencia en torno al comportamiento indefinido es la eliminación de verificación del puntero nulo del kernel de Linux .

Aunque se trata de optimizaciones agresivas, es importante tener en cuenta que, como el estándar de C ++ dice que el comportamiento indefinido es:

comportamiento para el cual esta Norma Internacional no impone requisitos

Lo que esencialmente significa que todo es posible y nota (el énfasis es mío ):

[...] El comportamiento indefinido permitido abarca desde ignorar completamente la situación con resultados impredecibles , hasta comportarse durante la traducción o la ejecución del progtwig de una manera documentada característica del entorno (con o sin la emisión de un mensaje de diagnóstico), hasta terminar una traducción o ejecución (con la emisión de un mensaje de diagnóstico). [...]

Para recibir una advertencia de gcc, necesitamos mover el cout fuera del ciclo y luego vemos la siguiente advertencia ( ver en vivo ):

 warning: iteration 3u invokes undefined behavior [-Waggressive-loop-optimizations] for(di=0; di<4;di++,delta=mc[di]){ } ^ 

lo que probablemente habría sido suficiente para proporcionar al PO la información suficiente para descubrir qué estaba pasando. Inconsistencias como esta son típicas de los tipos de comportamiento que podemos ver con un comportamiento indefinido. Para obtener una mejor comprensión de por qué tal waring puede ser inconsistente frente a un comportamiento indefinido. ¿Por qué no se puede advertir al optimizar en función de un comportamiento indefinido? es una buena lectura

Tenga en cuenta que -fno-aggressive-loop-optimizations está documentado en las notas de la versión de gcc 4.8 .

Dado que está incrementando di antes de usarlo para indexar mc , la cuarta vez a través del ciclo hará referencia a mc [4], que está más allá del final de su matriz, lo que a su vez podría generar un comportamiento problemático.

Tu tienes esto:

 for(int di=0; di<4; di++, delta=mc[di]) { cout< 

Pruebe esto en su lugar:

 for(int di=0; di<4; delta=mc[di++]) { cout< 

EDITAR:

Para aclarar lo que está sucediendo, desglosemos la iteración de su bucle For:

1ra iteración: Inicialmente di se establece en 0. Comprobación de comparación: ¿Es di menos de 4? Sí, de acuerdo, proceda. Incremente di por 1. Ahora di = 1. Coja el elemento "n" de mc [] y configúrelo como delta. Esta vez tomamos el 2. ° elemento, ya que este valor indexado es 1 y no 0. Por último, ejecutamos los bloques de código dentro del ciclo for.

2da iteración: Ahora di se establece en 1. Verificación de comparación: ¿Es di menos de 4? Sí y proceder Incremente di por 1. Ahora di = 2. Tome el elemento "n" de mc [] y configúrelo como delta. Esta vez tomamos el tercer elemento, ya que este valor indexado es 2. Finalmente, realizamos los bloques de código dentro del ciclo for.

3ra iteración: Ahora di se establece en 2. Verificación de comparación: ¿Es di menos de 4? Sí y proceder Incremente di por 1. Ahora di = 3. Tome el elemento "n" de mc [] y configúrelo como delta. Esta vez tomamos el 4 ° elemento dado que este valor indexado es 3. Finalmente, ejecutamos el / los bloque / s de código dentro del ciclo for.

4ta iteración: Ahora di se establece en 3. Verificación de comparación: ¿Di es menor que 4? Sí y proceder Incremente di por 1. Ahora di = 4. (¿Puede ver a dónde va esto?) Tome el elemento "n" de mc [] y configúrelo como delta. Esta vez tomamos el quinto elemento ya que este valor indexado es 4. Uh Oh, tenemos un problema; nuestro tamaño de matriz es solo 4. Delta ahora tiene basura y esto es un comportamiento indefinido o corrupción. Finalmente realice los bloques de código dentro del bucle for utilizando "delta de basura".

Quinta iteración. Ahora di se establece en 4. Verificación de comparación: ¿Es di menos de 4? No, sal del ciclo.

Corrupción al exceder los límites de la memoria contigua (matriz).

Es porque di ++ se ejecuta en la última ejecución del ciclo.

Por ejemplo;

 int di = 0; for(; di < 4; di++); // after the loop di == 4 // (inside the loop we see 0,1,2,3) // (inside the for statement, after di++, we see 1,2,3,4) 

Estás accediendo a mc [] cuando di == 4, por lo que se trata de un problema fuera de límites, que podría destruir parte de la stack y corromper la variable di.

una solución sería:

 for(int di = 0; di < 4; di++) { cout << di << endl; delta = mc[di]; }