2009-03-24 14 views
7

ich den folgenden Code habe:MSVC++: Fremdheit mit unsigned ints und Überlauf

#include <iostream> 

using namespace std; 

int main(int argc, char *argv[]) 
{ 
    string a = "a"; 
    for(unsigned int i=a.length()-1; i+1 >= 1; --i) 
    { 
     if(i >= a.length()) 
     { 
      cerr << (signed int)i << "?" << endl; 
      return 0; 
     } 
    } 
} 

Wenn ich in MSVC mit vollen Optimierungen zu kompilieren, die Ausgabe erhalte ich "-1?". Wenn ich im Debug-Modus (keine Optimierungen) kompiliere, bekomme ich keine Ausgabe (erwartet.)

Ich dachte der Standard garantiert, dass vorzeichenlose Integer in einer vorhersagbaren Weise übergelaufen, so dass, wenn i = (unsigned int) (- 1) , i + 1 = 0, und die Schleifenbedingung i + 1> = 1 schlägt fehl. Stattdessen ist der Test irgendwie vorbei. Ist das ein Compiler Bug, oder mache ich irgendwas Undefiniertes?

Antwort

8

Ich erinnere mich, dieses Problem im Jahr 2001 zu haben. Ich bin erstaunt, dass es immer noch da ist. Ja, das ist ein Compilerfehler.

Der Optimiser

i + 1 >= 1; 

Theoretisch können wir dies optimieren, sieht durch alle Konstanten auf der gleichen Seite setzen:

i >= (1-1); 

Da i nicht signiert ist, wird es immer sein größer als oder gleich Null.

Siehe diese Newsgroup-Diskussion here.

1

Ich bin mir nicht sicher, aber ich denke, dass Sie wahrscheinlich einen Fehler laufen.

Ich vermute, das Problem liegt darin, wie der Compiler die for Kontrolle behandelt. Ich konnte das Optimierungsprogramm vorstellen, zu tun:

for(unsigned int i=a.length()-1; i+1 >= 1; --i) // As written 

for (unsigned int i = a.length()-1; i >= 0; --i) // Noting 1 appears twice 

for (unsigned int i = a.length()-1; ; --i) // Because i >= 0 at all times 

Ob das, was passiert ist eine andere Sache, aber es könnte sein, genug, um den Optimierer zu verwirren.

würden Sie wahrscheinlich besser dran, indem eine Standard-Loop-Formulierung:

for (unsigned i = a.length()-1; i-- > 0;) 
4

ISO14882: 2003, Abschnitt 5, Absatz 5:

Wenn bei der Auswertung eines Ausdrucks, das Ergebnis ist nicht mathematisch definiert oder nicht im Bereich der darstellbaren Werte für seinen Typ, das Verhalten ist undefiniert, es sei denn, ein solcher Ausdruck ist ein konstanter Ausdruck (5.19), in diesem Fall ist das Programm schlecht gebildet.

(Schwerpunkt meiner.) Also, ja, das Verhalten ist undefiniert. Der Standard gibt keine Garantien für das Verhalten bei Integerüber-/unterlauf.

Edit: Der Standard scheint an anderer Stelle etwas konfliktbeladen.

Abschnitt 3.9.1.4 sagt:

Unsigned Integer, ohne Vorzeichen angegeben, so erhält die Gesetze der Arithmetik modulo 2 n gehorchen, wobei n die Anzahl der Bits in der Wertdarstellung dieser bestimmten Größe integer ist.

Aber Abschnitt 4.7.2 .3 sagt:

2) Wenn der Zieltyp nicht signiert ist, der sich ergebende Wert der am wenigsten unsigned integer kongruent mit der Source-Ganzzahl ist (modulo 2 n, wobei n ist die Anzahl der Bits, die zur Darstellung des Typs ohne Vorzeichen verwendet werden. [Anmerkung: In einer Zweierkomplementdarstellung ist diese Umwandlung konzeptionell und es gibt keine Änderung im Bitmuster (wenn keine Kürzung erfolgt). ]

3) Wenn der Zieltyp signiert ist, ist der Wert unverändert, wenn er im Zieltyp (und der Bitfeldbreite) dargestellt werden kann. Andernfalls ist der Wert implementierungsdefiniert.

(Hervorhebung von mir.)

+0

Hmm. Andere (auf verschiedenen Seiten) zitieren Abschnitt 4.7 des Standards: http://dev.feuvan.net/docs/isocpp/conv.html Sie verwenden dies um zu argumentieren, dass es definiert ist. –

+0

Ab 3.9.1 4 und seiner Fußnote hatte ich den Eindruck, dass vorzeichenlose Ganzzahlen eine Ausnahme sind: Da 1 in der Arithmetik von mod 2^n addiert wird, kann das Ergebnis nicht außerhalb des Wertebereichs liegen, oder? –

+0

Bei der Konvertierung in einen signierten Typ (wie hier) ist das Ergebnis implementierungsdefiniert. Da der Wert in einer signierten Operation außerhalb des Bereichs von * both * -Typen liegt, nehme ich an, dass dies unter Abschnitt 5 fällt. Der Compiler ist hier falsch, egal wie. – greyfade

0

Yup, Getestet habe ich diese nur auf Visual Studio 2005, verhält es sich auf jeden Fall anders in Debug und Veröffentlichung. Ich frage mich, ob 2008 es behebt.

Interessanterweise beschwerte es sich über Ihre implizite Umwandlung von size_t (Ergebnis von .length) zu unsigned int, hat aber kein Problem, schlechten Code zu generieren.