Warum ist (i + = i ++) für i = 0 gleich 0?

253

Nehmen Sie den folgenden Code (als Konsolenanwendung verwendbar):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

Das Ergebnis iist 0. Ich habe 2 erwartet (wie einige meiner Kollegen). Wahrscheinlich erstellt der Compiler eine Art Struktur, die zu iNull führt.

Der Grund, warum ich 2 erwartet habe, ist, dass in meinem Gedankengang die Aussage der rechten Hand zuerst ausgewertet wird und i mit 1 erhöht wird. Dann wird sie zu i hinzugefügt. Da ich bereits 1 bin, addiert es 1 zu 1. Also 1 + 1 = 2. Offensichtlich ist dies nicht das, was passiert.

Können Sie erklären, was der Compiler macht oder was zur Laufzeit passiert? Warum ist das Ergebnis Null?

Irgendein Haftungsausschluss: Ich bin mir absolut bewusst, dass Sie diesen Code nicht verwenden werden (und wahrscheinlich auch nicht sollten). Ich weiß, ich werde es nie tun. Trotzdem finde ich es interessant zu wissen, warum es so funktioniert und was genau passiert.

Peter
quelle
57
sollte das erwartete Ergebnis nicht 1 sein? i (0) + = i ++ (1) daher 0 + = 1 = 1
aleation
11
Es funktioniert wie erwartet
Steve ist ein D
177
Wie viele Variationen dieser Frage werden gestellt?
Mowwwalker
20
Die Vorinkrementierung erhöht den Wert, bevor die Aktion ausgeführt wird. I + = ++ Ich gebe Ihnen 1
Pierluc SS
21
Warum konzentrieren sich alle auf Vor- und Nachinkremente? Das "Seltsame" ist, dass der Wert von iauf der linken Seite von +="zwischengespeichert" wird, bevor die rechte Seite ausgewertet wird. Dies ist nicht intuitiv, da beispielsweise ein Kopiervorgang erforderlich wäre, wenn ies sich um ein Objekt handeln würde. (Bitte verstehen Sie mich nicht falsch: Ich stimme absolut zu, dass dies 0die richtige und standardkonforme Antwort ist.)
JohnB

Antworten:

425

Dies:

int i = 0;
i += i++

Kann so gesehen werden, wie Sie es tun (das Folgende ist eine grobe Vereinfachung):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Was tatsächlich passiert, ist mehr als das - werfen Sie einen Blick auf MSDN, 7.5.9 Postfix-Inkrementierungs- und Dekrementierungsoperatoren :

Die Laufzeitverarbeitung einer Postfix-Inkrementierungs- oder Dekrementierungsoperation der Form x ++ oder x-- besteht aus den folgenden Schritten:

  • Wenn x als Variable klassifiziert ist:

    • x wird ausgewertet, um die Variable zu erzeugen.
    • Der Wert von x wird gespeichert.
    • Der ausgewählte Operator wird mit dem gespeicherten Wert von x als Argument aufgerufen.
    • Der vom Bediener zurückgegebene Wert wird an der durch die Auswertung von x angegebenen Position gespeichert.
    • Der gespeicherte Wert von x wird zum Ergebnis der Operation.

Beachten Sie, dass der Postfix aufgrund der Rangfolge zuvor++ auftritt , das Ergebnis jedoch nicht verwendet wird (da der vorherige Wert von verwendet wird). +=i


Eine gründlichere Zerlegung i += i++der Teile, aus denen es besteht, erfordert, dass man weiß, dass beide +=und ++nicht atomar sind (dh keiner ist eine einzelne Operation), selbst wenn sie so aussehen, wie sie sind. Die Art und Weise, wie diese implementiert werden, umfasst temporäre Variablen, Kopien von ivor den Operationen - eine für jede Operation. (Ich werde die Namen iAddund iAssignfür die temporären Variablen verwenden, die für ++und verwendet werden+= jeweils).

Eine nähere Annäherung an das Geschehen wäre also:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;
Oded
quelle
3
@Oded Die ++Operation wird ausgeführt, bevor die Auswertung der Anweisung abgeschlossen ist. So +=überschreibt den Wert. Ist das passiert?
Anirudh Ramanathan
6
@Oded eigentlich sein : int i = 0; i = i + 1; (postfix) i = 0; (assignment). Wenn Sie i an anderer Stelle in dieser Anweisung verwenden würden, würde dies zu diesem Zeitpunkt 1 ergeben.
drch
@Cthulhu - Im Wesentlichen. Die Antwort von dtb geht ins Detail.
Oded
6
Ich kaufe diesen nicht. Die Antwort von @yoriy ist viel genauer. Zum einen sagen Sie in Ihrer Antwort, dass die letzte Zeile so wäre, wie i+1sie sein sollte i=i+1. Ist das nicht was i++?
Rückgewinnung
3
Der erste Teil der Antwort ist überflüssig. Ihr letztes Codebeispiel hätte es meiner Meinung nach tun können. +1 obwohl.
Corazza
194

Demontage des laufenden Codes:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Äquivalenter Code

Es wird mit demselben Code wie der folgende Code kompiliert:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Demontage des zweiten Codes (nur um zu beweisen, dass sie gleich sind)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Demontagefenster öffnen

Die meisten Benutzer wissen nicht oder erinnern sich nicht einmal daran, dass sie mithilfe von Visual Studio Disassembly den endgültigen Code für die In-Memory-Assembly sehen können Fensters sehen können. Es zeigt den Maschinencode, der ausgeführt wird, es ist nicht CIL.

Verwenden Sie dies beim Debuggen:

Debug (menu) -> Windows (submenu) -> Disassembly

Was passiert also mit Postfix ++?

Das Postfix ++ sagt, dass wir den Wert des Operanden nach der Auswertung erhöhen möchten ... dass jeder weiß ... was ein bisschen verwirrt, ist die Bedeutung von "nach der Auswertung". .

Was bedeutet also "nach der Bewertung" :

  • Andere Verwendungen des Operanden in derselben Codezeile müssen betroffen sein:
    • a = i++ + i Das zweite i wird durch das Inkrement beeinflusst
    • Func(i++, i) das zweite i ist betroffen
  • andere Verwendungen auf derselben Leitung respektieren den Kurzschlussoperator wie ||und &&:
    • (false && i++ != i) || i == 0 Das dritte i ist von i ++ nicht betroffen, da es nicht ausgewertet wird

Was bedeutet also : i += i++;?

Es ist das gleiche wie i = i + i++;

Die Reihenfolge der Bewertung ist:

  1. Speichern Sie i + i (das ist 0 + 0)
  2. Inkrementiere i (i wird 1)
  3. Weisen Sie i den Wert von Schritt 1 zu (i wird 0)

Nicht dass das Inkrement verworfen wird.

Was bedeutet : i = i++ + i;?

Dies ist nicht dasselbe wie im vorherigen Beispiel. Der 3 ..i ist vom Inkrement betroffen.

Die Reihenfolge der Bewertung ist:

  1. Speichern Sie i (das ist 0)
  2. Inkrementiere i (i wird 1)
  3. Speichern Sie den Wert von Schritt 1 + i (dh 0 + 1).
  4. Weisen Sie i den Wert von Schritt 3 zu (i wird 1)
Miguel Angelo
quelle
22
+ 1 ++ - für reine Hardcore-Dissektion. Chuck Norris wäre stolz :) Ich denke, Sie gehen davon aus, dass das OP auf Intel läuft, aber kein Mono-Port ...
StuartLC
19
C # hat eine genau definierte Auswertungsreihenfolge für den Ausdruck, und der Objektcode implementiert nur diese Reihenfolge. Die Ausgabe des Maschinencodes ist nicht der Grund oder die Erklärung für die Auswertungsreihenfolge.
Kaz
8
Der Maschinencode macht es leicht zu verstehen, wie die Reihenfolge der Auswertung IMO implementiert wird.
Kevin
5
@StuartLC Ich sehe, was du dort gemacht hast. Schade um diese verworfene Gegenstimme.
Steffan Donal
2
a++ + aist nicht dasselbe wie a + a++weil dies keine reine Mathematik mehr ist. Das Kommutativitätsgesetz in der Algebra berücksichtigt nicht die Möglichkeit, dass Variablen den Wert in der Mitte eines Ausdrucks ändern. Die Mathematik lässt sich nur dann gut auf die Programmierung abbilden, wenn die Programmierung eine funktionale Programmierung ist. Und auch dann nicht, weil die Repräsentation eingeschränkt ist. Zum Beispiel verhalten sich Gleitkommazahlen manchmal wie Real und manchmal nicht. Auch ohne Nebenwirkungen brechen Kommutativitäts- und Assoziativitätsgesetze, die für reelle Zahlen in der Mathematik gelten, Gleitkommazahlen.
Kaz
61
int i = 0;
i += i++;

wird wie folgt bewertet:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

dh iwird zweimal geändert: einmal durch den i++Ausdruck und einmal durch die +=Anweisung.

Aber die Operanden der +=Anweisung sind

  • der Wert ivor der Auswertung von i++(linke Seite von +=) und
  • der Wert ivor der Auswertung von i++(rechts von +=).
dtb
quelle
Ah, das ist eine fantastische Erklärung. Erinnert mich daran, als ich an einem stapelbasierten Taschenrechner mit umgekehrter Poliernotation gearbeitet habe.
Nathan
36

Zuerst i++kehrt 0. Dann iwird um 1 erhöht schließlich iauf den Anfangswert der festgelegt ist idie 0 plus der Wert i++zurückgegeben, die Null zu ist. 0 + 0 = 0.

Jong
quelle
2
Aber es ist i += i++;nicht i = i++;so, also wird der Wert von i++(0) addiert iund nicht " iwird auf den zurückgegebenen Wert gesetzt i++". Nun ist die Frage, wenn die Wertschöpfung i++zurück i, wird isein der erhöhte Wert oder der nicht-inkrementierte Wert? Die Antwort, mein Freund, steht in den Spezifikationen.
Daniel Fischer
Stimmt, ich werde es reparieren. Aber trotzdem ist da i = 0anfangs i += somethinggleichbedeutend mit dem i = 0 + somethingwas ist i = something.
Jong
32

Dies ist einfach eine Bewertung des abstrakten Syntaxbaums von links nach rechts von unten nach oben. Konzeptionell wird der Baum des Ausdrucks von oben nach unten verschoben, aber die Auswertung erfolgt, wenn die Rekursion den Baum von unten nach oben öffnet.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

Die Auswertung beginnt mit der Berücksichtigung des Wurzelknotens +=. Das ist der Hauptbestandteil des Ausdrucks. Der linke Operand von +=muss ausgewertet werden, um die Stelle zu bestimmen, an der wir die Variable speichern, und um den vorherigen Wert zu erhalten, der Null ist. Als nächstes muss die rechte Seite ausgewertet werden.

Die rechte Seite ist ein ++Operator nach dem Inkrementieren . Es hat einen Operanden, ider sowohl als Quelle eines Wertes als auch als Ort, an dem ein Wert gespeichert werden soll, ausgewertet wird. Der Bediener wertet ein i, findet es 0und speichert es folglich an 1diesem Ort. Es gibt den vorherigen Wert zurück.0 gemäß seiner Semantik der Rückgabe des vorherigen Werts zurück.

Jetzt ist die Steuerung wieder beim +=Bediener. Es hat jetzt alle Informationen, um seinen Betrieb abzuschließen. Es kennt den Ort, an dem das Ergebnis gespeichert werden soll (den Speicherort von i) sowie den vorherigen Wert, und es hat den Wert, der zum vorherigen Wert hinzugefügt werden muss, nämlich 0. Also iendet mit Null.

Wie Java hat C # einen sehr albernen Aspekt der C-Sprache bereinigt, indem die Reihenfolge der Auswertung festgelegt wurde. Von links nach rechts, von unten nach oben: Die offensichtlichste Reihenfolge, die von Codierern erwartet wird.

Kaz
quelle
+1: Ich stimme Ihnen zu, außer dass jeder Codierer erwartet, dass ... ich erwartet habe, dass es dasselbe ist wie mit so etwas: SetSum(ref i, Inc(ref i))mit int SetSum(ref int a, int b) { return a += b; }und int Inc(ref int a) { return a++; }... natürlich erwarte ich das nicht mehr.
Miguel Angelo
Auch was ich erwartet habe ist inkonsistent! Es wäre nicht gleich Set(ref i, Sum(i, Inc(ref i)))mit int Set(ref int a, int b) { return a = b; }und int Sum(int a, int b) { return a + b; }.
Miguel Angelo
Vielen Dank; Sie deuten auf einen Fehler / eine Unvollständigkeit in meiner Antwort hin, den ich beheben muss.
Kaz
Das Problem dabei SetSumist, dass der linke Operand nicht ausgewertet wird i, sondern nur seine Adresse verwendet wird. Dies entspricht also nicht einer vollständigen Auswertung des Operanden von links nach rechts. Du brauchst so etwas wie SetSum(ref i, i, PostInc(ref i)). Das zweite Argument von SetSumist der hinzuzufügende Wert, wobei wir nur iden vorherigen Wert von angeben i. SetSumist einfach int SetSum(ref int dest, int a, int b) { return dest = a + b; }.
Kaz
Die Verwechslung tritt (zumindest für mich) mit dem Operator + = auf, weil der Zuweisungsoperator eine Auswertung von rechts nach links hat (z. B. a = b = c = d). Man kann sich also vorstellen, dass + = der gleichen Regel folgt. als atomare Operation (wie ich es mit meiner SetSum-Methode getan habe) ... aber was tatsächlich passiert, ist, dass C # a += bin a = a + b... übersetzt wird, was zeigt, dass der Operator + = nicht atomar ist ... es ist nur syntaktischer Zucker.
Miguel Angelo
30

Weil i++zuerst der Wert zurückgegeben und dann erhöht wird. Aber nachdem ich auf 1 gesetzt wurde, setzen Sie es zurück auf 0.

Yuriy Faktorovich
quelle
17

Die Post-Inkrement-Methode sieht ungefähr so ​​aus

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

Wenn Sie also aufrufen i++, iist dies ein Inkrement, aber der ursprüngliche Wert wird zurückgegeben, in Ihrem Fall wird 0 zurückgegeben.

Ash Burlaczenko
quelle
12

Einfache Antwort

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;
Praveen Kumar Purushothaman
quelle
12

i ++ bedeutet: Rückgabe des Wertes von i DANN inkrementieren.

i + = i ++ bedeutet: Nimm den aktuellen Wert von i. Fügen Sie das Ergebnis von i ++ hinzu.

Fügen wir nun i = 0 als Startbedingung hinzu. i + = i ++ wird jetzt folgendermaßen ausgewertet:

  1. Was ist der aktuelle Wert von i? Es ist 0. Speichern Sie es, damit wir das Ergebnis von i ++ hinzufügen können.
  2. Evaluiere i ++ (ergibt 0, da dies der aktuelle Wert von i ist)
  3. Laden Sie den gespeicherten Wert und fügen Sie das Ergebnis von Schritt 2 hinzu. (addiere 0 zu 0)

Hinweis: Am Ende von Schritt 2 ist der Wert von i tatsächlich 1. In Schritt 3 verwerfen Sie ihn jedoch, indem Sie den Wert von i laden, bevor er inkrementiert wurde.

Im Gegensatz zu i ++ gibt ++ i den inkrementierten Wert zurück.

Daher würde i + = ++ ich Ihnen 1 geben.

Carl
quelle
Es ist Hilfe Full
Sonsha
11

Der Post-Fix-Inkrement-Operator ++gibt der Variablen einen Wert im Ausdruck und führt dann das von Ihnen zugewiesenei Inkrement erneut mit dem Wert null (0) aus , wodurch die inkrementierte Eins (1) überschrieben wird , sodass Sie Null erhalten. Weitere Informationen zum Inkrementoperator finden Sie in ++ Operator (MSDN).

Adil
quelle
8

i += i++;wird gleich Null sein, weil es das ++danach tut .

i += ++i; werde es vorher tun

Wes Cossick
quelle
4
Wenn dies ++später der Fall ist , würde ich das Ergebnis erwarten 1.
Comecme
8

Das ++ - Postfix wird ivor dem Inkrementieren +=ausgewertet und nur ieinmal ausgewertet .

Daher ist 0 + 0 = 0, wie ies ausgewertet und verwendet wird, bevor es inkrementiert wird, da das Postfix-Format von ++verwendet wird. iVerwenden Sie das Präfix form ( ++i), um zuerst inkrementiert zu werden .

(Auch nur eine Anmerkung: Sie sollten nur 1 erhalten, da 0 + (0 + 1) = 1)

Referenzen: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)

Nate Koppenhaver
quelle
8

Was C # tut und das "Warum" der Verwirrung

Ich habe auch erwartet, dass der Wert 1 ist ... aber einige Untersuchungen in dieser Angelegenheit haben einige Punkte geklärt.

Berücksichtigen Sie die folgenden Methoden:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

Ich habe erwartet, i += i++dass das genauso sein wird wie SetSum(ref i, Inc(ref i)). Der Wert von i nach dieser Anweisung ist 1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

Aber dann bin ich zu einem anderen Schluss gekommen ... i += i++ist eigentlich dasselbe wie i = i + i++... also habe ich mit diesen Funktionen ein weiteres ähnliches Beispiel erstellt:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

Nach dem Aufruf ist Set(ref i, Sum(i, Inc(ref i)))der Wert von i 0 :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

Dies erklärt nicht nur, was C # tut ... sondern auch, warum viele Leute damit verwechselt wurden ... einschließlich mir.

Miguel Angelo
quelle
2
Bitte fügen Sie dies zu Ihrer ursprünglichen Antwort hinzu. Es bringt keinen Vorteil, wenn Sie es als separate Antwort haben.
CasperOne
2
Ich habe dies getan, um die andere Antwort nicht zu verschmutzen, da es sich um den dekompilierten Code handelt ... während ich in diesem einen anderen Ansatz versuchte, um die Dinge zu erklären. Was denken Sie? Soll ich die andere Antwort bearbeiten und diese anhängen? Vielleicht stellen Sie dieses voran ... ich weiß es nicht! Danke für Vorschläge!
Miguel Angelo
7

Eine gute Mnemonik, an die ich mich immer erinnere, ist die folgende:

Wenn ++steht nach dem Ausdruck, gibt sie den Wert , den es war vor . Also der folgende Code

int a = 1;
int b = a++;

ist 1, weil aes 1 war, bevor es durch das ++Stehen danach erhöht wurde a. Die Leute nennen diesen Beitrag Fix Notation. Es gibt auch eine Pre - Fix - Notation, wo die Dinge sind genau das Gegenteil: Wenn ++Ständen vor , der Ausdruck den Wert zurückgibt , dass es nach der Operation:

int a = 1;
int b = ++a;

b ist zwei hier.

Für Ihren Code bedeutet dies also

int i = 0;
i += (i++);

i++gibt 0 zurück (wie oben beschrieben), also 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyers beschreibt den Unterschied zwischen diesen beiden Notationen in "Effektive C ++ - Programmierung". Intern i++merkt sich (postfix) den Wert iund ruft die Präfixnotation ( ++i) auf und gibt den alten Wert zurück i. Aus diesem Grunde , ist immer verwenden soll , ++iin forSchleifen (obwohl ich denke , dass alle modernen Compiler übersetzen i++zu ++iin forSchleifen).

Carsten
quelle
1
Ich habe getestet int i = 0; i += (++i)und iist auf eins anstatt auf zwei eingestellt. Es macht mir auch Sinn, da das Präfix anstelle des postfix verwenden , nicht an der Tatsache , dass, wenn Sie schreiben , i += (++i)um aus i = i + (++i), die ivor ausgewertet wird ++i, was in i = 0 + (++i)und schließlich i = 0 + 1.
Wutz
6

Die einzige richtige Antwort auf Ihre Frage lautet: Weil sie undefiniert ist.

Ok, bevor ihr mich alle verbrennt ..

Sie alle haben geantwortet, warum i+=i++es in Ordnung und logisch ist, daraus zu resultieren i=0.

Ich war versucht, jede einzelne Ihrer Antworten abzustimmen, aber der von mir berechnete Reputationshit wäre zu hoch.

Warum bin ich so sauer auf euch Leute? Nicht aufgrund dessen, was Ihre Antworten erklären.
Ich meine, jede Antwort, die ich las, hatte bemerkenswerte Anstrengungen unternommen, um das Unmögliche zu erklären. Ich applaudiere!

Aber was ist das Ergebnis? ist es ein intuitives Ergebnis - ist es ein akzeptables Ergebnis?

Jeder von euch hat den "nackten König" gesehen und ihn irgendwie als vernünftigen König akzeptiert.

Ihr seid alle FALSCH!

i+=i++;Ergebnis in 0ist undefiniert.

ein Fehler im Sprachbewertungsmechanismus, wenn Sie so wollen ... oder noch schlimmer! ein Fehler im Design.

Willst du einen Beweis? natürlich willst du!

int t=0; int i=0; t+=i++; //t=0; i=1

Nun ... das ist ein intuitives Ergebnis! weil wir es zuerst tmit einem Wert bewertet haben und erst nach Bewertung und Zuweisung die Nachoperation durchgeführt wurde - rational, nicht wahr?

ist es rational, dass: i=i++und i=idas gleiche Ergebnis für liefern i?

während t=i++und t=ihaben unterschiedliche Ergebnisse füri .

Die Nachoperation sollte nach der Auswertung der Anweisung erfolgen.
Deshalb:

int i=0;
i+=i++;

Sollte das gleiche sein, wenn wir geschrieben haben:

int i=0;
i = i + i ++;

und daher das gleiche wie:

int i=0;
i= i + i;
i ++;

und daher das gleiche wie:

int i=0;
i = i + i;
i = i + 1;

Jedes Ergebnis, das nicht ist 1 auf einen Fehler im Complier oder einen Fehler im Sprachdesign hinweist, wenn wir rational denken - MSDN und viele andere Quellen sagen uns jedoch: "Hey - das ist undefiniert!"

Bevor ich fortfahre, wird selbst diese Reihe von Beispielen, die ich gegeben habe, von niemandem unterstützt oder anerkannt. Dies sollte jedoch auf intuitive und rationale Weise das Ergebnis sein.

Der Codierer sollte keine Kenntnis davon haben, wie die Baugruppe geschrieben oder übersetzt wird!

Wenn es so geschrieben ist, dass die Sprachdefinitionen nicht berücksichtigt werden, ist es ein Fehler!

Und zum Schluss habe ich dies aus Wikipedia kopiert: Inkrement- und Dekrementierungsoperatoren :
Da der Inkrementierungs- / Dekrementierungsoperator seinen Operanden ändert, kann die mehrmalige Verwendung eines solchen Operanden innerhalb desselben Ausdrucks zu undefinierten Ergebnissen führen . In Ausdrücken wie x - ++ x ist beispielsweise nicht klar, in welcher Reihenfolge die Subtraktions- und Inkrementoperatoren ausgeführt werden sollen. Situationen wie diese werden noch schlimmer, wenn der Compiler Optimierungen anwendet, was dazu führen kann, dass die Reihenfolge der Ausführung der Operationen anders ist als vom Programmierer beabsichtigt.

Und deshalb.

Die richtige Antwort ist, dass dies nicht verwendet werden sollte! (wie es UNDEFINIERT ist!)

Ja .. - Es hat unvorhersehbare Ergebnisse, selbst wenn der C # -Konformator versucht, es irgendwie zu normalisieren.

Ich habe keine Dokumentation von C # gefunden, die das Verhalten beschreibt, das Sie alle als normales oder genau definiertes Verhalten der Sprache dokumentiert haben. Was ich gefunden habe, ist genau das Gegenteil!

[ kopiert aus der MSDN-Dokumentation für Postfix Increment and Decrement Operators: ++ und - ]

Wenn ein Postfix-Operator auf ein Funktionsargument angewendet wird, kann nicht garantiert werden , dass der Wert des Arguments inkrementiert oder dekrementiert wird, bevor er an die Funktion übergeben wird. Weitere Informationen finden Sie in Abschnitt 1.9.17 des C ++ - Standards.

Beachten Sie diese Wörter nicht garantiert ...

Vergib mir, wenn diese Antwort arrogant erscheint - ich bin keine arrogante Person. Ich denke nur, dass Tausende von Menschen hierher kommen, um zu lernen, und die Antworten, die ich lese, werden sie irreführen und ihre Logik und ihr Verständnis des Themas schädigen.

GY
quelle
Ich bin nicht sicher, ob ich 100% folge, aber Sie verweisen auf die C ++ - Dokumentation, aber meine Frage betraf C #. Die Dokumentation dazu finden Sie hier .
Peter
Ich bezog mich in meiner Antwort auf C #. Über den von Ihnen angegebenen Link: Das Ergebnis von x ++ oder x-- ist der Wert von x vor der Operation, während das Ergebnis von ++ x oder --x der Wert von x nach der Operation ist. In beiden Fällen hat x selbst nach der Operation den gleichen Wert. deutlich zeigt dies nicht der Fall ist , wenn die Prüfung .. denn i=++ianderes Ergebnis liefern wird i=i++. Deshalb steht meine Antwort.
GY
Aha, ok, aber es ist verwirrend, wenn Sie auf die C ++ - Dokumentation verweisen. Sie sagen also, dass die Spezifikation nicht korrekt implementiert wurde?
Peter
Nein. Ich sage, dass es gemäß der Spezifikation undefiniert ist und die Verwendung von undefiniert zu undefinierten Ergebnissen führt.
GY
In C ++ undefiniert, aber C # sagt, dass es nach der Operation der gleiche Wert sein sollte , nein? Das ist nicht dasselbe wie undefiniert (aber ich stimme zu, dass Sie es nicht verwenden sollten, siehe meinen Haftungsausschluss, ich habe nur versucht zu verstehen, was los ist).
Peter
4

Der ++ - Operator nach der Variablen macht sie zu einem Postfix-Inkrement. Das Inkrementieren erfolgt nach allem anderen in der Anweisung, dem Hinzufügen und Zuweisen. Wenn Sie stattdessen ++ vor die Variable setzen, geschieht dies, bevor der Wert von i ausgewertet wurde, und gibt Ihnen die erwartete Antwort.

KeithS
quelle
2
Das ++passiert nicht nach der +=Anweisung, sondern während der Ausführung der +=Anweisung. Das ist der Grund, warum die Auswirkungen der ++Übersteuerung durch die +=.
dtb
Mit ++ ergibt sich tatsächlich 1, nicht 2 (meine ursprünglich 'erwartete Antwort').
Peter
Sieht aus wie die Aufgabe += die Änderung aufgrund des Vor- oder Nachinkrements im Ausdruck überschreibt .
Steven Lu
4

Die Berechnungsschritte sind:

  1. int i=0 // Auf 0 initialisiert
  2. i+=i++ //Gleichung
  3. i=i+i++ // nach Vereinfachung der Gleichung durch den Compiler
  4. i=0+i++ // Ich Wert Substitution
  5. i=0+0 // i ++ ist 0 wie unten erklärt
  6. i=0 // Endergebnis i = 0

Hier ist anfangs der Wert von i0. WKT,i++ ist nichts anderes als: Verwenden Sie zuerst den iWert und erhöhen Sie dann den iWert um 1. Er verwendet also den iWert 0, während er berechnet, i++und erhöht ihn dann um 1. Er ergibt also einen Wert von 0.

Suresh M.
quelle
3

Es gibt zwei Möglichkeiten:

Die erste Option: Wenn der Compiler die Anweisung wie folgt liest,

i++;
i+=i;

dann ist das Ergebnis 2.

Zum

else if
i+=0;
i++;

das Ergebnis ist 1.

NEO
quelle
5
Beides ist nicht das tatsächliche Ergebnis.
Steven Lu
3

Seien Sie sehr vorsichtig: Lesen Sie die C- FAQ : Was Sie versuchen (Mischzuweisung und ++dieselbe Variable), ist nicht nur nicht spezifiziert, sondern auch undefiniert (was bedeutet, dass der Compiler alles tun kann bei der Auswertung !, Nicht nur geben "vernünftige" Ergebnisse).

Bitte lesen Sie Abschnitt 3 . Der ganze Abschnitt ist eine Lektüre wert! Insbesondere 3.9, was die Implikation von nicht spezifiziert erklärt. Abschnitt 3.3 gibt Ihnen eine kurze Zusammenfassung dessen, was Sie mit "i ++" und dergleichen tun können und was nicht.

Abhängig von den Interna des Compilers erhalten Sie möglicherweise 0, 2, 1 oder sogar alles andere! Und da es undefiniert ist, ist es für sie in Ordnung, dies zu tun.

Olivier Dulac
quelle
oops, c # ... Ich wurde von der "gcc" abgeworfen, die einige durchlaufen haben, um den Code zu zerlegen.
Olivier Dulac
1
Ich vermisste, dass es auch C # war, aber die Antwort gefiel mir trotzdem.
Iain Collins
1
@Iain: Danke, ich glaube auch, dass es sich gelohnt hat, die Antwort verfügbar zu halten. Viele Menschen wissen nichts darüber (oder über diese großartige FAQ aus der besten Zeit des Usenet, in der die meisten Leute mit Kenntnissen über ein Thema dasselbe besuchten Ort, um es zu aktualisieren)
Olivier Dulac
3

Die obigen Antworten enthalten viele hervorragende Argumente. Ich habe gerade einen kleinen Test durchgeführt und möchte sie mit Ihnen teilen

int i = 0;
i+ = i++;

Hier zeigt Ergebnis i 0 Ergebnis. Betrachten Sie nun die folgenden Fälle:

Fall 1:

i = i++ + i; //Answer 1

Früher dachte ich, dass der obige Code diesem ähnelt, also ist die Antwort auf den ersten Blick 1, und die Antwort von i für diesen ist wirklich 1.

Fall 2:

i = i + i++; //Answer 0 this resembles the question code.

Hier kommt der Inkrement-Operator nicht in den Ausführungspfad, im Gegensatz zu früheren Fällen, in denen i ++ die Möglichkeit hat, vor dem Hinzufügen ausgeführt zu werden.

Ich hoffe das hilft ein bisschen. Vielen Dank

ThomasBecker
quelle
2

In der Hoffnung, dies aus einer Perspektive der C-Programmierung 101 zu beantworten.

Sieht für mich so aus, als ob es in dieser Reihenfolge passiert:

  1. iwird als 0 ausgewertet, was dazu führt, dass i = 0 + 0die Inkrementierungsoperation i++"in die Warteschlange gestellt" wird, aber die Zuweisung von 0 zu iist auch noch nicht erfolgt.
  2. Das Inkrement i++erfolgt
  3. Die Zuweisung i = 0von oben erfolgt und überschreibt effektiv alles, was # 2 (das Nachinkrement) getan hätte.

Nun, # 2 kann tatsächlich nie passieren (wahrscheinlich nicht?), Da der Compiler wahrscheinlich erkennt, dass es keinen Zweck erfüllt, aber dies könnte vom Compiler abhängig sein. In beiden Fällen haben andere, sachkundigere Antworten gezeigt, dass das Ergebnis korrekt ist und dem C # -Standard entspricht, es ist jedoch nicht definiert, was hier für C / C ++ geschieht.

Wie und warum liegt außerhalb meines Fachwissens, aber die Tatsache, dass die zuvor bewertete Zuordnung auf der rechten Seite nach dem Nachinkrement erfolgt, ist hier wahrscheinlich verwirrend.

Außerdem würden Sie nicht erwarten, dass das Ergebnis 2 ist, es sei denn, Sie haben es getan, ++ianstatt i++ich glaube.

gkimsey
quelle
1
Die Vorinkrement-Version erzeugt ein Ergebnis von 2mit C ++: ideone.com/8dH8tf
Steven Lu
Das macht Sinn. Das Vorinkrement ist jedoch eine geringfügig weniger komplizierte Situation als das Nachinkrement.
Gkimsey
2

Einfach gesagt,

i ++ addiert 1 zu "i", nachdem der Operator "+ =" abgeschlossen wurde.

Was Sie wollen, ist ++ i, so dass 1 zu "i" hinzugefügt wird, bevor der Operator "+ =" ausgeführt wird.

Seesharper
quelle
0
i=0

i+=i

i=i+1

i=0;

Dann wird die 1 hinzugefügt i.

i + = i ++

Also vor dem 1. zum Hinzufügen i, um iden Wert von 0. Nur nahm , wenn wir 1 hinzufügen , bevor, ierhält den Wert 0.

i+=++i

i=2
lernen, was
quelle
-4

Die Antwort iwird sein 1.

Schauen wir uns an, wie:

Anfangs i=0;.

Während der Berechnung i +=i++;nach dem Wert von haben wir dann so etwas wie 0 +=0++;, also wird nach dem Operator die Priorität 0+=0zuerst ausgeführt und das Ergebnis wird sein 0.

Dann wird der Inkrementoperator angewendet , wie 0++, wie , 0+1und der Wert isein wird 1.

Shivam Sharma
quelle
3
Diese Antwort ist falsch. Sie erhalten keine 1, weil bei der 0 += 0++;Zuweisung nach dem Inkrement, ++aber mit dem Wert von i vor interpretiert wird ++(weil es sich um einen
Postoperator handelt
2
Entschuldigung, aber das ist falsch. Lesen Sie meine Frage und Sie werden sehen, dass das Ergebnis 0 ist. Wenn Sie den Code ausführen, sehen Sie, dass er effektiv 0 ist.
Peter