Frage

Kann mir jemand erklären, warum dieser Code 14 druckt? Ich war gerade von einem anderen Schüler gefragt und kann es nicht herausgefunden.

int i = 5;
i = ++i + ++i;
cout<<i;
War es hilfreich?

Lösung

Die Reihenfolge der Nebenwirkungen ist nicht definiert in C ++. Zusätzlich eine Variable zweimal in einem einzigen Ausdruck zu modifizieren kein definiertes Verhalten hat (siehe C ++ Standard , §5.0.4, physikalische Seite 87 / logische Seite 73).

Lösung: Verwenden Sie keine Nebenwirkungen in komplexen Ausdruck, nicht mehr als eine in Einfältigen verwenden. Und es schadet nicht, alle Warnungen aktivieren die Compiler können Sie: Hinzufügen -Wall (gcc) oder /Wall /W4 (Visual C ++) in die Befehlszeile eine passende Warnung ergibt:

test-so-side-effects.c: In function 'main':
test-so-side-effects.c:5: warning: operation on 'i' may be undefined
test-so-side-effects.c:5: warning: operation on 'i' may be undefined

Offensichtlich ist der Code kompiliert zu:

i = i + 1;
i = i + 1;
i = i + i;

Andere Tipps

Das ist nicht definiertes Verhalten, das Ergebnis auf dem Compiler variiert je Sie verwenden. Siehe zum Beispiel C ++ FAQ Lite .

In einigen der Antworten / Kommentare hat es eine Diskussion über die Bedeutung des Begriffs ‚nicht definiertes Verhalten‘ gewesen und ob das macht das Programm ungültig. So bin Entsendung mir diese ziemlich lange Antwort Detaillierung genau das, was der Standard mit einigen Anmerkungen sagt. Ich hoffe, es ist nicht zu langweilig ...

Die angegebenen Bits des Standard stammte aus dem aktuellen C ++ Standard (ISO / IEC 14882: 2003). Es gibt eine ähnliche Formulierung in dem C-Standard.

Nach dem C ++ Standard Modifizieren ein Wert mehr als einmal innerhalb eines Satzes von Sequenzpunkten führen zu undefiniertem Verhalten (Abschnitt 5 Ziffer 4):

  

Soweit nicht anders angegeben, die Reihenfolge der   Auswertung der Operanden einzelner   Betreiber und Unterausdrücken von   einzelne Ausdrücke und die Reihenfolge   in denen Nebenwirkungen stattfinden, ist   unspecified.53) Zwischen dem vorherigen   und nächste Sequenz zeigt einen skalar   Objekt hat seinen gespeicherten Wert haben   höchstens einmal durch die modifizierte   Auswertung eines Ausdrucks.   Darüber hinaus wird der vorherige Wert   Zugriff nur den Wert zu bestimmen,   zu speichernden. Die Anforderungen dieser   Absatz gilt für jede erfüllt sein   zulässige Ordnung der   Unterausdrücke eines vollständigen Ausdrucks;   ansonsten ist das Verhalten nicht definiert.   [Beispiel:

i = v[i++]; // the behavior is unspecified
i = 7, i++, i++; // i becomes 9
i = ++i + 1; // the behavior is unspecified
i = i + 1; // the value of i is incremented
     

-Ende Beispiel]

Man beachte, dass das zweite Beispiel, „i = 7, i++, i++;“ definiert ist, da der Komma-Operator eine Sequenz Punkt ist.

Hier ist, was der C ++ Standard sagt, dass 'nicht definiertes Verhalten' bedeutet:

  

1.3.12 undefiniertes Verhalten [defns.undefined]

     

Verhalten, wie zum Beispiel bei der Verwendung eines fehlerhaften Programmkonstrukt oder fehlerhafte Daten, für die diese entstehen könnten   Internationale Norm legt keine Anforderungen. Undefiniertes Verhalten auch erwartet werden kann, wenn dies   Internationale Norm lässt die Beschreibung jeder explizite Definition des Verhaltens. [Anmerkung: zulässige undefined   Verhalten reicht von der Situation völlig mit unvorhersehbaren Ergebnissen zu ignorieren, zu verhalten während   Übersetzung oder die Programmausführung in einer dokumentierten Weise charakteristisch für die Umgebung (mit oder ohne   die Ausgabe einer Diagnosemeldung), die Übersetzung oder die Ausführung (mit der Ausgabe einer zum Beenden   Diagnosemeldung). Viele fehlerhaften Programmkonstrukte Sie erzeugen nicht undefiniertes Verhalten; sie sind zu diagnostizierenden erforderlich. ]

Mit anderen Worten, ist der Compiler frei zu tun, was er will, einschließlich

  1. eine Fehlermeldung ausspuckt,
  2. etwas zu tun Implementierung definiert und dokumentiert,
  3. mit völlig unvorhersehbaren Ergebnissen

Der zweite Punkt umfasst Spracherweiterungen, die die meisten Compiler haben, aber natürlich sind nicht im Standard definiert ist.

Also ich denke, dass streng etwas zu sprechen, das nicht definiertes Verhalten zeigt, ist nicht ‚illegal‘, aber nach meiner Erfahrung, wann immer es ist schon etwas in einem C / C ++ Programm, das zeigt ‚undefiniertes Verhalten‘ (es wäre denn es eine Erweiterung ist) - es ist ein Fehler ist . Ich denke, dass ein solches Konstrukt illegal Aufruf nicht verwirrend, irreführend oder verfehlt.

Auch ich denke, versuchen zu erklären, was der Compiler den Wert zu erreichen, macht 14 nicht besonders hilfreich ist, da dies den Punkt verfehlt. Der Compiler konnte fast alles tun; in der Tat ist es wahrscheinlich, dass der Compiler ein anderes Ergebnis erreichen kann, wenn es unterschiedliche Optimierungsoptionen laufen gelassen (oder kann einen Code erzeugen, der abstürzt? - wer weiß).

Für diejenigen, die einige zusätzliche Hinweise oder einen Appell an Autorität wollen, sind hier einige Hinweise:

Steve Summit (Maintainer der comp.lang.c Häufig gestellte Fragen) lange, langen Antwort zu diesem Thema von 1995:

Hier ist, was Bjarne Stroustrup in der Sache zu sagen hat:


Fußnote . Die C ++ Standard verwendet das Wort 'illegal' genau einmal - wenn eine Differenz zwischen C beschreiben ++ und Standard C in Bezug auf die Verwendung von static oder extern mit Typdeklarationen

Einfache ... Sie Compiler prüft BEIDE Schritten vor, die Summe durchgeführt wird, ohne die Zwischenergebnisse zwischenspeichern. Dies bedeutet, dass, wenn Sie i zweimal hinzufügen, ist es nun, den Wert von 7 hat.

Wenn Sie das tun

int j=++i; 
int k=++i;

i = j+k;

Sie werden 13 sehen, wie erwartet.

Auf Ihrem speziellen Compiler, es ist die Wahl zunächst beide der ++ Operationen zu tun, dann der Zugabe. Es ist die Interpretation des Codes wie:

int i = 5;
++i;
++i;
i = i + i;
cout << i;

Das ist völlig in Ordnung.

Eine bessere Frage wäre, ist es immer 14 gehen werden?

int i = 5;
i = ++i + ++i;
cout<<i;

i = ++i   + ++i   ;
i = ++(5) + ++(5) ;
i =    6  +    6  ;
i = 12;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++i   +   (6) ;
i = ++(6) +    6  ;
i =   (7) +    6  ;
i = 13;

i = ++i   + ++i   ;
i = ++i   + ++(5) ;
i = ++(6) +   (6) ;
i =   (7) +   (7) ;
i = 14;

Aller Wahrscheinlichkeit nach wird es wahrscheinlich 14 werden, denn es macht leicht mehr Sinn.

Ich denke, dass, wenn das Problem aus der Sicht des Syntaxbaums, die Antwort auf das Problem wird deutlicher suchen:

i
|
=
|
+
|
einstelliger Ausdruck - einstelliger Ausdruck

einstelliger Ausdruck: unärer Operator Ausdruck

In unserem Fall kocht Ausdruck auf die Variable nach unten i.

Nun, was passiert ist, dass beide unary Ausdruck derselben Operanden ändern, so dass der Code zweimal tut ++ i, wenn vor der Zugabe der Ergebnisse beiden unäre Ausdrücke den unäre Ausdrücke auswertet.

Also, was der Code tut, ist in der Tat

++ i;
++ i;
i = i + i;

Für i = 5 das heißt

i = i + 1; // i <- 6
i = i + 1; // i <- 7
i = i + i; // i <- 14

Da der Präfix Schritt hat Vorrang:

int i = 5;
i = i+1; // First ++i, i is now 6
i = i+1; // Second ++i, i is now 7
i = i + i // i = 7 + 7
cout << i // i = 14
 i = i++ + i; //11  

 i = i++ + i++; //12

 i = i++ + ++i; //13

 i = ++i + i++; //13

 i = ++i + ++i; //14    
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top