Was ist “int i = 1;Warum (i >= 60 * 60 * 1000 / 1 * 1000)” wahr?
Lesezeit: 5 Minuten
Celebi
Erstens ist es meine Schuld, zwei konstante Ausdrücke ohne Klammern zu definieren:
#define BIG_INTERVAL 60 * 60 * 1000
#define SMALL_INTERVAL 1 * 1000
int i = 1;
if (i >= BIG_INTERVAL / SMALL_INTERVAL - 1)
{
printf("Oops!\n");
}
Das if Anweisung nach der Makroerweiterung ist if(i >= 60 * 60 * 1000 / 1 * 1000 - 1).
Das ist nicht meine Absicht. Aber ich finde etwas seltsam, wenn ich schreibe if (i >= 3600000000 - 1). Ist es falsch.
Welche Art ist 60 * 60 * 1000 / 1 * 1000 - 1 ? int?
Und hier haben wir den Grund, warum vernünftige Programmierer darauf verzichten #define für Konstanten.
– jalf
14. Juli 2011 um 6:20 Uhr
@jalf: Oder du könntest einfach daran denken, sie mit Klammern zu umgeben …
– icktoofay
14. Juli 2011 um 6:22 Uhr
Sie könnten. So wie es in Ordnung ist, sich selbst in den Fuß zu schießen, wenn Sie dafür sorgen, dass ein Arzt in der Nähe ist? Warum machen Sie es nicht einfach richtig und verwenden eine tatsächliche typisierte Konstante (sagen wir a static const intoder vielleicht eine Aufzählung)?
– jalf
14. Juli 2011 um 6:50 Uhr
@icktoofay: Das Problem ist, dass Klammern dieses spezielle Problem beheben, bis Sie es vergessen, und selbst wenn Sie dies nicht tun, werden Sie mit Makros um die Ecke auf ein anderes Problem stoßen. Es gibt einfach zu viele Dinge, die man im Auge behalten muss, wenn man Makros verwendet. Betrachten Sie dieses Makro, das in einem der Projekte enthalten war, an denen ich gearbeitet habe: #define for_all( iterator_t, it, container ) for ( iterator_t it = (container).begin(); it != (container).end(); ++it )das verwendet wird als: for_all( std::vector<int>::const_iterator, it, v ) std::cout << *it; einfach … oder?
– David Rodríguez – Dribeas
14. Juli 2011 um 7:28 Uhr
Habe die Frage auf “c” umgetaggt. Ihr Code ist NICHT c++; in C++ sollten Sie printf() und #define’s nicht verwenden
– Andreas Bonini
14. Juli 2011 um 13:16 Uhr
Alle Operatoren an ints Rückkehr int. Also ja, 60 * 60 * 1000 / 1 * 1000 - 1 ist ein int. Aber das erwartete Ergebnis von 3599999999 ist zu groß für eine intsodass der Ausdruck tatsächlich zu -694967297 ausgewertet wird (unter der Annahme von 32-Bit int und Zweierkomplement).
Dies passiert nicht mit einem Literal 3600000000 weil Integer-Literale größer als INT_MAX sind von einer Art, die kann den vollen Wert halten.
“ein Typ, der den vollen Wert enthalten kann” – WENN ein solcher Typ existiert. Es gibt keine Garantie für Literale, die überschreiten ULONG_MAX.
– MSalter
14. Juli 2011 um 8:56 Uhr
Integer-Überlauf (im Gegensatz zum Stapel) 🙂
– Dunkler Stern1
19. Juli 2011 um 18:39 Uhr
Eben 60 * 60 * 1000 kann zu groß für eine sein int; scheint meine Antwort.
– Keith Thompson
7. August 2011 um 0:09 Uhr
Petar Iwanow
60 * 60 * 1000 / 1 * 1000 – 1 = 3600000 * 1000 – 1, wodurch der int-Typ überläuft, sodass das Ergebnis alles sein kann (in Ihrem Fall ist es negativ, muss es aber nicht).
Um das zu erreichen, was Sie wollen, setzen Sie ( ):
+1 für das Vorschlagen von Klammern – das erklärt das eigentliche Problem – obwohl das Vermeiden von Makros ein noch besserer Rat wäre. Dennoch lässt “überläuft den int-Typ, also ist er negativ” diesen Ton so klingen, als würde ersteres immer letzteres implizieren. Umständlich, aber formal ist es ein undefiniertes Verhalten für vorzeichenbehaftete ganzzahlige Typen und in der Praxis bedeutet es im Allgemeinen, dass Sie Müll erhalten, der positiv oder negativ sein kann … hier sind 3.600.000 * 1.000 zufällig <2 ^ 32, aber > 2 ^ 31, also ist es allgemein negativ Plattformen mit 32-Bit ints….
– Toni Delroy
14. Juli 2011 um 6:27 Uhr
kazinix
Hier meine Testergebnisse:
60 * 60 * 1000 / 1 * 1000 will result to -694967296
(60 * 60 * 1000) / (1*1000) will result to 3600
Es gibt ein Problem mit Ihrer Operation, dem Vorrang von Berechnungen.
Vielleicht möchten Sie einen Blick auf die Rangfolge der C++-Operatoren werfen http://msdn.microsoft.com/en-us/library/126fe14k%28v=vs.80%29.aspx. Sie werden den Grund finden, warum das Ergebnis -694967296 wurde, was meiner Meinung nach ein Effekt des Überlaufs ist.
@muntoo – Hier habe ich es erklärt.
– Kazinix
14. Juli 2011 um 6:40 Uhr
Wenn Sie einen Compiler verwenden, bei dem int 64 Bit ist, werden Sie feststellen, dass das Ergebnis Ihres Ausdrucks falsch ist. Wenn Sie einen Compiler verwenden, bei dem int 32 Bit oder 16 Bit ist, hat Ihr Ausdruck ein undefiniertes Verhalten, da der Überlauf von signierten ints nicht umlaufen muss. Wahrscheinlich hat sich deine gerade herumgewickelt, aber das muss nicht sein.
3600000000 ist eine Konstante, die zur Kompilierzeit sichtbar ist. Wenn also int nur 32 Bit beträgt, muss Ihr Compiler long long wählen (oder einfach long, wenn long 64 Bit beträgt). Ihr anderer Ausdruck wird also mit genügend Bits ausgewertet, um einen Überlauf zu vermeiden, und das Ergebnis ist korrekt.
Könnte sein, dass Sie die Größe eines int überlaufen, das 2147 m oder so signiert ist, was bedeutet, dass wenn Sie über die Darstellung gehen, wird dies negativ. Wie aus anderen Antworten hervorgeht, bewirkt die Division beim Erweitern nichts, also umgeben Sie die Makrodefinitionen mit Klammern
ca. 2147 Millionen , nicht 2,7 Millionen (unter der Annahme von 4 Byte Int)
– Mitch Weizen
14. Juli 2011 um 6:19 Uhr
Danke, ich sollte wirklich keine Fragen beantworten, wenn ich müde bin 😛
– Jesus Ramos
14. Juli 2011 um 6:20 Uhr
Es könnte von Interesse sein, dass dies wahrscheinlich für das OP geschieht, weil die interne Darstellung verwendet wird Zweierkomplementaber das Überlaufverhalten im Allgemeinen ist undefiniert.
– Maxpm
14. Juli 2011 um 6:21 Uhr
@Maxpm Sie haben völlig Recht, da einige Compiler den Überlauf konstanter Werte tatsächlich behandeln, indem sie ihn -1 oder einem anderen Wert zuweisen
– Jesus Ramos
14. Juli 2011 um 6:23 Uhr
Jarod Elliot
Sie verlassen höchstwahrscheinlich den gültigen Wertebereich für ein signed int – 3600000000 ist eine ziemlich große Zahl!
In diesem Fall wird der Wert zum kleinsten negativen Wert für den int-Datentyp.
Dies wird dazu führen, dass Ihre Aussage wahr ist.
ca. 2147 Millionen , nicht 2,7 Millionen (unter der Annahme von 4 Byte Int)
– Mitch Weizen
14. Juli 2011 um 6:19 Uhr
Danke, ich sollte wirklich keine Fragen beantworten, wenn ich müde bin 😛
– Jesus Ramos
14. Juli 2011 um 6:20 Uhr
Es könnte von Interesse sein, dass dies wahrscheinlich für das OP geschieht, weil die interne Darstellung verwendet wird Zweierkomplementaber das Überlaufverhalten im Allgemeinen ist undefiniert.
– Maxpm
14. Juli 2011 um 6:21 Uhr
@Maxpm Sie haben völlig Recht, da einige Compiler den Überlauf konstanter Werte tatsächlich behandeln, indem sie ihn -1 oder einem anderen Wert zuweisen
– Jesus Ramos
14. Juli 2011 um 6:23 Uhr
Nicol Bola
Jedes der Argumente dieses Ausdrucks ist eine ganze Zahl, also ist das Ergebnis eine ganze Zahl.
Ja, das sind ganze Zahlen. Genauer gesagt sind sie es ints. (Das Wort integer umfasst eine Reihe von Typen, von char bis zu long long; int' is a particular type, not *just* an abbreviation of Ganzzahl”.)
– Keith Thompson
7. August 2011 um 0:02 Uhr
13905800cookie-checkWas ist “int i = 1;Warum (i >= 60 * 60 * 1000 / 1 * 1000)” wahr?yes
Und hier haben wir den Grund, warum vernünftige Programmierer darauf verzichten
#define
für Konstanten.– jalf
14. Juli 2011 um 6:20 Uhr
@jalf: Oder du könntest einfach daran denken, sie mit Klammern zu umgeben …
– icktoofay
14. Juli 2011 um 6:22 Uhr
Sie könnten. So wie es in Ordnung ist, sich selbst in den Fuß zu schießen, wenn Sie dafür sorgen, dass ein Arzt in der Nähe ist? Warum machen Sie es nicht einfach richtig und verwenden eine tatsächliche typisierte Konstante (sagen wir a
static const int
oder vielleicht eine Aufzählung)?– jalf
14. Juli 2011 um 6:50 Uhr
@icktoofay: Das Problem ist, dass Klammern dieses spezielle Problem beheben, bis Sie es vergessen, und selbst wenn Sie dies nicht tun, werden Sie mit Makros um die Ecke auf ein anderes Problem stoßen. Es gibt einfach zu viele Dinge, die man im Auge behalten muss, wenn man Makros verwendet. Betrachten Sie dieses Makro, das in einem der Projekte enthalten war, an denen ich gearbeitet habe:
#define for_all( iterator_t, it, container ) for ( iterator_t it = (container).begin(); it != (container).end(); ++it )
das verwendet wird als:for_all( std::vector<int>::const_iterator, it, v ) std::cout << *it;
einfach … oder?– David Rodríguez – Dribeas
14. Juli 2011 um 7:28 Uhr
Habe die Frage auf “c” umgetaggt. Ihr Code ist NICHT c++; in C++ sollten Sie printf() und #define’s nicht verwenden
– Andreas Bonini
14. Juli 2011 um 13:16 Uhr