c++ - Programa de comportamiento extraño en IDEs en línea
undefined-behavior integer-overflow (4)
Asumiré que los compiladores en línea usan GCC o un compilador compatible. Por supuesto, cualquier otro compilador también puede hacer la misma optimización, pero la documentación de GCC explica bien lo que hace:
-faggressive-loop-optimizations
Esta opción le dice al optimizador de bucles que use restricciones de lenguaje para derivar límites para la cantidad de iteraciones de un bucle. Esto supone que el código de bucle no invoca un comportamiento indefinido, por ejemplo, causando desbordamientos de enteros firmados o accesos de matriz fuera del límite. Los límites para el número de iteraciones de un bucle se usan para guiar las optimizaciones de desenrollado y pelado del bucle y la prueba de salida del bucle. Esta opción está activada de forma predeterminada.
Esta opción simplemente permite hacer suposiciones basadas en casos donde UB está probado. Para aprovechar esas suposiciones, es posible que sea necesario habilitar otras optimizaciones, como el plegado constante.
El desbordamiento de entero firmado tiene un comportamiento indefinido.
El optimizador pudo probar que cualquier valor de
i
mayor que 173 causaría UB, y debido a que puede suponer que no hay UB, también puede suponer que nunca es mayor que 173. Luego puede demostrar que
i < 300
siempre es cierto, por lo que la condición del bucle se puede optimizar.
¿Por qué 4169 y no algún otro valor?
Esos sitios probablemente limitan el número de líneas de salida (o caracteres o bytes) que muestran y comparten el mismo límite.
Me he encontrado con el siguiente programa C ++ ( source ):
#include <iostream>
int main()
{
for (int i = 0; i < 300; i++)
std::cout << i << " " << i * 12345678 << std::endl;
}
Parece un programa simple y proporciona la salida correcta en mi máquina local, es decir, algo como:
0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574
Pero, en IDEs en línea como codechef , da el siguiente resultado:
0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597
¿Por qué el ciclo
for
no termina en 300?
Además, este programa siempre termina en
4169
.
¿Por qué
4169
y no algún otro valor?
El compilador puede suponer que no se producirá un comportamiento indefinido y, dado que el desbordamiento firmado es UB, puede suponer que nunca
i * 12345678 > INT_MAX
, por lo tanto, también
i <= INT_MAX / 12345678 < 300
y, por lo tanto, elimine la comprobación
i < 300
.
Está invocando
un comportamiento indefinido
probablemente en la iteración 174 dentro de su bucle
for
ya que el valor
int
máximo probablemente es
2147483647
pero la expresión
174 * 123456789
evalúa como
2148147972
que es un comportamiento indefinido ya que no hay un desbordamiento de entero con signo.
Entonces está observando los efectos de UB, particularmente con el compilador GCC con banderas de optimización establecidas en su caso.
Es probable que el compilador lo haya advertido al emitir la siguiente advertencia:
warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]
Elimine los
-O2
optimización (
-O2
) para observar resultados diferentes.
"El comportamiento indefinido es indefinido". (do)
Un compilador utilizado en codechef parece usar la siguiente lógica:
- El comportamiento indefinido no puede suceder.
-
i * 12345678
desborda y da como resultado UB sii > 173
(suponiendoint
s de 32 bits). -
Por lo tanto, nunca puedo exceder
173
. -
Por lo tanto,
i < 300
es superfluo y puede reemplazarse portrue
.
El bucle en sí parece ser infinito. Aparentemente, codechef simplemente detiene el programa después de un período de tiempo específico o trunca la salida.