Ich bin auf das folgende C ++ - Programm ( Quelle ) gestoßen :
#include <iostream>
int main()
{
for (int i = 0; i < 300; i++)
std::cout << i << " " << i * 12345678 << std::endl;
}
Es sieht aus wie ein einfaches Programm und gibt die richtige Ausgabe auf meinem lokalen Computer aus, dh so etwas wie:
0 0
1 12345678
2 24691356
...
297 -628300930
298 -615955252
299 -603609574
Bei Online-IDEs wie codechef wird jedoch die folgende Ausgabe ausgegeben:
0 0
1 12345678
2 24691356
...
4167 -95167326
4168 -82821648
4169 -7047597
Warum endet die for
Schleife nicht bei 300? Auch dieses Programm endet immer am 4169
. Warum 4169
und nicht irgendein anderer Wert?
c++
undefined-behavior
integer-overflow
Arpanmangal
quelle
quelle
Antworten:
Ich gehe davon aus, dass die Online-Compiler GCC oder einen kompatiblen Compiler verwenden. Natürlich darf auch jeder andere Compiler die gleiche Optimierung durchführen, aber die GCC-Dokumentation erklärt gut, was er tut:
Diese Option ermöglicht lediglich Annahmen, die auf Fällen basieren, in denen UB nachgewiesen ist. Um diese Annahmen nutzen zu können, müssen möglicherweise andere Optimierungen aktiviert werden, z. B. die konstante Faltung.
Der vorzeichenbehaftete Ganzzahlüberlauf hat ein undefiniertes Verhalten. Der Optimierer konnte beweisen, dass ein Wert von
i
mehr als 173 UB verursachen würde, und da er annehmen kann, dass es keine UB gibt, kann er auch annehmen, dass eri
niemals größer als 173 ist. Er kann dann weiter beweisen, dass diesi < 300
immer wahr ist, und so kann der Schleifenzustand weg optimiert werden.Diese Sites begrenzen wahrscheinlich die Anzahl der Ausgabezeilen (oder Zeichen oder Bytes), die sie anzeigen, und teilen zufällig dieselbe Grenze.
quelle
i
mehr als 173 gibt, warum gibt er dann keine Warnung aus, anstatt eine sinnlose Optimierung durchzuführen?"Undefiniertes Verhalten ist undefiniert." (c)
Ein auf codechef verwendeter Compiler scheint folgende Logik zu verwenden:
i * 12345678
läuft über und führt zu UB ifi > 173
(unter der Annahme von 32 Bitint
s).i
kann nie überschreiten173
.i < 300
ist überflüssig und kann durch ersetzt werdentrue
.Die Schleife selbst scheint unendlich zu sein. Anscheinend stoppt codechef das Programm nur nach einer bestimmten Zeit oder schneidet die Ausgabe ab.
quelle
2^16
. Anscheinend ist das ein Zufall, dass beide die Ausgabe auf2^16
Zeichen kürzen .Sie rufen undefiniertes Verhalten wahrscheinlich bei der 174. Iteration in Ihrer
for
Schleife auf, da derint
Maximalwert wahrscheinlich2147483647
noch174 * 123456789
Ausdruck ist, für2148147972
den ein undefiniertes Verhalten vorliegt, da kein vorzeichenbehafteter Ganzzahlüberlauf vorliegt. Sie beobachten also die Auswirkungen von UB, insbesondere beim GCC-Compiler mit in Ihrem Fall gesetzten Optimierungsflags. Es ist wahrscheinlich, dass der Compiler Sie mit der folgenden Warnung davor gewarnt hätte:warning: iteration 174 invokes undefined behavior [-Waggressive-loop-optimizations]
Entfernen Sie die (
-O2
) Optimierungsflags, um unterschiedliche Ergebnisse zu beobachten.quelle
Der Compiler kann davon ausgehen, dass kein undefiniertes Verhalten auftritt, und da der signierte Überlauf UB ist, kann er davon ausgehen, dass niemals
i * 12345678 > INT_MAX
, also auchi <= INT_MAX / 12345678 < 300
und daher die Prüfung entfernt wirdi < 300
.quelle