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 i
ist 0. Ich habe 2 erwartet (wie einige meiner Kollegen). Wahrscheinlich erstellt der Compiler eine Art Struktur, die zu i
Null 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.
i
auf der linken Seite von+=
"zwischengespeichert" wird, bevor die rechte Seite ausgewertet wird. Dies ist nicht intuitiv, da beispielsweise ein Kopiervorgang erforderlich wäre, wenni
es sich um ein Objekt handeln würde. (Bitte verstehen Sie mich nicht falsch: Ich stimme absolut zu, dass dies0
die richtige und standardkonforme Antwort ist.)Antworten:
Dies:
Kann so gesehen werden, wie Sie es tun (das Folgende ist eine grobe Vereinfachung):
Was tatsächlich passiert, ist mehr als das - werfen Sie einen Blick auf MSDN, 7.5.9 Postfix-Inkrementierungs- und Dekrementierungsoperatoren :
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 voni
vor den Operationen - eine für jede Operation. (Ich werde die NameniAdd
undiAssign
für die temporären Variablen verwenden, die für++
und verwendet werden+=
jeweils).Eine nähere Annäherung an das Geschehen wäre also:
quelle
++
Operation wird ausgeführt, bevor die Auswertung der Anweisung abgeschlossen ist. So+=
überschreibt den Wert. Ist das passiert?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.i+1
sie sein solltei=i+1
. Ist das nicht wasi++
?Demontage des laufenden Codes:
Äquivalenter Code
Es wird mit demselben Code wie der folgende Code kompiliert:
Demontage des zweiten Codes (nur um zu beweisen, dass sie gleich sind)
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" :
a = i++ + i
Das zweite i wird durch das Inkrement beeinflusstFunc(i++, i)
das zweite i ist betroffen||
und&&
:(false && i++ != i) || i == 0
Das dritte i ist von i ++ nicht betroffen, da es nicht ausgewertet wirdWas bedeutet also :
i += i++;
?Es ist das gleiche wie
i = i + i++;
Die Reihenfolge der Bewertung ist:
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:
quelle
a++ + a
ist nicht dasselbe wiea + 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.wird wie folgt bewertet:
dh
i
wird zweimal geändert: einmal durch deni++
Ausdruck und einmal durch die+=
Anweisung.Aber die Operanden der
+=
Anweisung sindi
vor der Auswertung voni++
(linke Seite von+=
) undi
vor der Auswertung voni++
(rechts von+=
).quelle
Zuerst
i++
kehrt 0. Danni
wird um 1 erhöht schließlichi
auf den Anfangswert der festgelegt isti
die 0 plus der Werti++
zurückgegeben, die Null zu ist. 0 + 0 = 0.quelle
i += i++;
nichti = i++;
so, also wird der Wert voni++
(0) addierti
und nicht "i
wird auf den zurückgegebenen Wert gesetzti++
". Nun ist die Frage, wenn die Wertschöpfungi++
zurücki
, wirdi
sein der erhöhte Wert oder der nicht-inkrementierte Wert? Die Antwort, mein Freund, steht in den Spezifikationen.i = 0
anfangsi += something
gleichbedeutend mit demi = 0 + something
was isti = something
.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.
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,i
der sowohl als Quelle eines Wertes als auch als Ort, an dem ein Wert gespeichert werden soll, ausgewertet wird. Der Bediener wertet eini
, findet es0
und speichert es folglich an1
diesem 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 voni
) sowie den vorherigen Wert, und es hat den Wert, der zum vorherigen Wert hinzugefügt werden muss, nämlich0
. Alsoi
endet 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.
quelle
SetSum(ref i, Inc(ref i))
mitint SetSum(ref int a, int b) { return a += b; }
undint Inc(ref int a) { return a++; }
... natürlich erwarte ich das nicht mehr.Set(ref i, Sum(i, Inc(ref i)))
mitint Set(ref int a, int b) { return a = b; }
undint Sum(int a, int b) { return a + b; }
.SetSum
ist, dass der linke Operand nicht ausgewertet wirdi
, sondern nur seine Adresse verwendet wird. Dies entspricht also nicht einer vollständigen Auswertung des Operanden von links nach rechts. Du brauchst so etwas wieSetSum(ref i, i, PostInc(ref i))
. Das zweite Argument vonSetSum
ist der hinzuzufügende Wert, wobei wir nuri
den vorherigen Wert von angebeni
.SetSum
ist einfachint SetSum(ref int dest, int a, int b) { return dest = a + b; }
.a += b
ina = a + b
... übersetzt wird, was zeigt, dass der Operator + = nicht atomar ist ... es ist nur syntaktischer Zucker.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.quelle
Die Post-Inkrement-Methode sieht ungefähr so aus
Wenn Sie also aufrufen
i++
,i
ist dies ein Inkrement, aber der ursprüngliche Wert wird zurückgegeben, in Ihrem Fall wird 0 zurückgegeben.quelle
Einfache Antwort
quelle
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:
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.
quelle
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).quelle
i += i++;
wird gleich Null sein, weil es das++
danach tut .i += ++i;
werde es vorher tunquelle
++
später der Fall ist , würde ich das Ergebnis erwarten1
.Das ++ - Postfix wird
i
vor dem Inkrementieren+=
ausgewertet und nuri
einmal ausgewertet .Daher ist 0 + 0 = 0, wie
i
es ausgewertet und verwendet wird, bevor es inkrementiert wird, da das Postfix-Format von++
verwendet wird.i
Verwenden 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 (++)
quelle
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:
Ich habe erwartet,
i += i++
dass das genauso sein wird wieSetSum(ref i, Inc(ref i))
. Der Wert von i nach dieser Anweisung ist 1 :Aber dann bin ich zu einem anderen Schluss gekommen ...
i += i++
ist eigentlich dasselbe wiei = i + i++
... also habe ich mit diesen Funktionen ein weiteres ähnliches Beispiel erstellt:Nach dem Aufruf ist
Set(ref i, Sum(i, Inc(ref i)))
der Wert von i 0 :Dies erklärt nicht nur, was C # tut ... sondern auch, warum viele Leute damit verwechselt wurden ... einschließlich mir.
quelle
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 Codeist 1, weil
a
es 1 war, bevor es durch das++
Stehen danach erhöht wurdea
. 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:b
ist zwei hier.Für Ihren Code bedeutet dies also
i++
gibt 0 zurück (wie oben beschrieben), also0 + 0 = 0
.Scott Meyers beschreibt den Unterschied zwischen diesen beiden Notationen in "Effektive C ++ - Programmierung". Intern
i++
merkt sich (postfix) den Werti
und ruft die Präfixnotation (++i
) auf und gibt den alten Wert zurücki
. Aus diesem Grunde , ist immer verwenden soll ,++i
infor
Schleifen (obwohl ich denke , dass alle modernen Compiler übersetzeni++
zu++i
infor
Schleifen).quelle
int i = 0; i += (++i)
undi
ist 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 ausi = i + (++i)
, diei
vor ausgewertet wird++i
, was ini = 0 + (++i)
und schließlichi = 0 + 1
.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 resultiereni=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 in0
ist 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
t
mit einem Wert bewertet haben und erst nach Bewertung und Zuweisung die Nachoperation durchgeführt wurde - rational, nicht wahr?ist es rational, dass:
i=i++
undi=i
das gleiche Ergebnis für lieferni
?während
t=i++
undt=i
haben unterschiedliche Ergebnisse füri
.Die Nachoperation sollte nach der Auswertung der Anweisung erfolgen.
Deshalb:
Sollte das gleiche sein, wenn wir geschrieben haben:
und daher das gleiche wie:
und daher das gleiche wie:
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.
quelle
i=++i
anderes Ergebnis liefern wirdi=i++
. Deshalb steht meine Antwort.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.
quelle
++
passiert nicht nach der+=
Anweisung, sondern während der Ausführung der+=
Anweisung. Das ist der Grund, warum die Auswirkungen der++
Übersteuerung durch die+=
.+=
die Änderung aufgrund des Vor- oder Nachinkrements im Ausdruck überschreibt .Die Berechnungsschritte sind:
int i=0
// Auf 0 initialisierti+=i++
//Gleichungi=i+i++
// nach Vereinfachung der Gleichung durch den Compileri=0+i++
// Ich Wert Substitutioni=0+0
// i ++ ist 0 wie unten erklärti=0
// Endergebnis i = 0Hier ist anfangs der Wert von
i
0. WKT,i++
ist nichts anderes als: Verwenden Sie zuerst deni
Wert und erhöhen Sie dann deni
Wert um 1. Er verwendet also deni
Wert 0, während er berechnet,i++
und erhöht ihn dann um 1. Er ergibt also einen Wert von 0.quelle
Es gibt zwei Möglichkeiten:
Die erste Option: Wenn der Compiler die Anweisung wie folgt liest,
dann ist das Ergebnis 2.
Zum
das Ergebnis ist 1.
quelle
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.
quelle
Die obigen Antworten enthalten viele hervorragende Argumente. Ich habe gerade einen kleinen Test durchgeführt und möchte sie mit Ihnen teilen
Hier zeigt Ergebnis i 0 Ergebnis. Betrachten Sie nun die folgenden Fälle:
Fall 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:
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
quelle
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:
i
wird als 0 ausgewertet, was dazu führt, dassi = 0 + 0
die Inkrementierungsoperationi++
"in die Warteschlange gestellt" wird, aber die Zuweisung von 0 zui
ist auch noch nicht erfolgt.i++
erfolgti = 0
von 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,
++i
anstatti++
ich glaube.quelle
2
mit C ++: ideone.com/8dH8tfEinfach 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.
quelle
Dann wird die 1 hinzugefügt
i
.i + = i ++
Also vor dem 1. zum Hinzufügen
i
, umi
den Wert von 0. Nur nahm , wenn wir 1 hinzufügen , bevor,i
erhält den Wert 0.quelle
Die Antwort
i
wird sein1
.Schauen wir uns an, wie:
Anfangs
i=0;
.Während der Berechnung
i +=i++;
nach dem Wert von haben wir dann so etwas wie0 +=0++;
, also wird nach dem Operator die Priorität0+=0
zuerst ausgeführt und das Ergebnis wird sein0
.Dann wird der Inkrementoperator angewendet , wie
0++
, wie ,0+1
und der Werti
sein wird1
.quelle
0 += 0++;
Zuweisung nach dem Inkrement,++
aber mit dem Wert von i vor interpretiert wird++
(weil es sich um einen