pero - C++ error de compilación?
error 5 en c++ (4)
Tengo el siguiente código:
#include <iostream>
#include <complex>
using namespace std;
int main() {
complex<int> delta;
complex<int> 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 se detenga, pero genera 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 solo comento
,delta=mc[di]
, obtengo "0, 1, 2, 3" como de costumbre.
¿Cuál es el problema con la asignación inocente?
Estoy usando Ideone.com g ++ C ++ 14 con la opción -O2.
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 conducir a un comportamiento problemático.
Es porque di ++ se ejecuta en la última ejecución del bucle.
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á accediendo a mc [] cuando di == 4, por lo que es un problema fuera de límites, potencialmente destruyendo parte de la pila y corrompiendo la variable di.
una solución sería:
for(int di = 0; di < 4; di++) {
cout << di << endl;
delta = mc[di];
}
Esto se debe a un comportamiento indefinido, está accediendo a la matriz
mc
fuera de los límites en la última iteración de su bucle.
Algunos compiladores pueden realizar una optimización de bucle agresiva en torno a los supuestos de comportamiento no definido.
La lógica sería similar a la siguiente:
-
Acceder a
mc
fuera de los límites es un comportamiento indefinido - Asumir que no hay comportamiento indefinido
-
Por lo tanto,
di < 4
siempre es cierto ya que de lo contrariomc[di]
invocaría un comportamiento indefinido
gcc con la optimización activada y el uso del
-fno-aggressive-loop-optimizations
hace que el comportamiento del bucle infinito desaparezca (
-fno-aggressive-loop-optimizations
en vivo
).
Si bien un
ejemplo en vivo con optimización pero sin optimizaciones de bucle -fno-agresivo
exhibe el comportamiento de bucle infinito que observa.
Un
ejemplo en vivo del código de godbolt
muestra que la verificación
di < 4
se elimina y se reemplaza con un jmp incondicional:
jmp .L6
Esto es casi idéntico al caso descrito en
los puntos de referencia de GCC anteriores a 4.8 Breaks Broken SPEC 2006
.
Los comentarios a este artículo son excelentes y vale la pena leerlos.
Señala que clang captó el caso en el artículo usando
-fsanitize=undefined
que no puedo reproducir para este caso, pero gcc usando
-fsanitize=undefined
sí (
verlo en vivo
).
Probablemente el error más infame en torno a un optimizador que hace una inferencia sobre el comportamiento indefinido es la
eliminación de la comprobación de puntero nulo del kernel de Linux
.
Aunque se trata de optimizaciones agresivas, es importante tener en cuenta que, como dice el estándar C ++, el comportamiento indefinido es:
comportamiento para el cual esta Norma Internacional no impone requisitos
Lo que esencialmente significa que todo es posible y señala ( énfasis mío ):
[...] El comportamiento indefinido permitido varía desde ignorar la situación por completo con resultados impredecibles , a comportarse durante la traducción o la ejecución del programa de una manera documentada característica del entorno (con o sin la emisión de un mensaje de diagnóstico), hasta finalizar una traducción o ejecución (con la emisión de un mensaje de diagnóstico). [...]
Para obtener una advertencia de gcc, necesitamos mover el
cout
fuera del ciclo y luego vemos la siguiente advertencia (
verla 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 OP con suficiente información para descubrir lo que estaba sucediendo. La inconsistencia como esta es típica de los tipos de comportamiento que podemos ver con un comportamiento indefinido. Para comprender mejor por qué dicha advertencia puede ser inconsistente frente a un comportamiento indefinido ¿Por qué no puede advertir cuando se optimiza en base a un comportamiento indefinido? Es una buena lectura.
Tenga en cuenta que las
-fno-aggressive-loop-optimizations
están documentadas en las
notas de la versión de gcc 4.8
.
Tu tienes esto:
for(int di=0; di<4; di++, delta=mc[di]) {
cout<<di<<endl;
}
Intenta esto en su lugar:
for(int di=0; di<4; delta=mc[di++]) {
cout<<di<<endl;
}
EDITAR:
Para aclarar lo que está sucediendo Vamos a analizar la iteración de su bucle for:
Primera iteración: inicialmente di se establece en 0. Comprobación de comparación: ¿di es menor que 4? Sí, bien, proceda. Incremente di en 1. Ahora di = 1. Tome el elemento "enésimo" de mc [] y configúrelo como delta. Esta vez estamos agarrando el segundo elemento ya que este valor indexado es 1 y no 0. Finalmente realice el / los bloque / es de código dentro del bucle for.
Segunda iteración: ahora di está configurado en 1. Comprobación de comparación: ¿di es menor que 4? Si y proceder. Incremente di en 1. Ahora di = 2. Tome el elemento "enésimo" de mc [] y configúrelo como delta. Esta vez estamos agarrando el tercer elemento ya que este valor indexado es 2. Finalmente, realice el / los bloque / es de código dentro del bucle for.
3ª iteración: ahora di está configurado en 2. Comprobación de comparación: ¿di es menor que 4? Si y proceder. Incremente di en 1. Ahora di = 3. Tome el elemento "enésimo" de mc [] y configúrelo como delta. Esta vez estamos agarrando el cuarto elemento ya que este valor indexado es 3. Finalmente, realice el / los bloque / es de código dentro del bucle for.
4ta iteración: ahora di está configurado en 3. Comprobación de comparación: ¿di es menor que 4? Si y proceder. Incremente di en 1. Ahora di = 4. (¿Puede ver hacia dónde va esto?) Tome el elemento "enésimo" de mc [] y configúrelo como delta. Esta vez estamos agarrando 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 este es un comportamiento indefinido o corrupción. Finalmente realice el / los bloque / es de código dentro del bucle for utilizando "delta de basura".
5ta iteración. Ahora di está configurado en 4. Comprobación de comparación: ¿di es menor que 4? No, salir del circuito.
La corrupción al exceder los límites de la memoria contigua (matriz).