Was ist x nach "x = x ++"?

285

Was passiert (hinter den Vorhängen), wenn dies ausgeführt wird?

int x = 7;
x = x++;

Das heißt, wenn eine Variable nachträglich inkrementiert und sich selbst in einer Anweisung zugewiesen wird? Ich habe dies kompiliert und ausgeführt. xist auch nach der gesamten Aussage noch 7 . In meinem Buch heißt es, dass xinkrementiert wird!

Michael
quelle
9
Versuchen Sie Folgendes: int x = 7; x = ++x;Natürlich ist der Code immer noch schrecklich, Sie müssen ihn nicht neu zuweisen. int x = 7; x++;reicht.
Stivlo
7
Dies ist eine wirklich schlechte Vorgehensweise. Erhöhen Sie die Variable nicht in derselben Zeile, in der Sie sie verwenden.
Yousf
5
Ich würde es vorziehen, es zu verwenden x += 1, außer vielleicht in Schleifen. for(int x=0; x<7; x++)
Svish
2
@andyortlieb gibt es kein Objekt, nur einen Grundwert.
Fortan

Antworten:

301

xwird inkrementiert. Aber Sie weisen den alten Wert von xback in sich selbst zu.


x = x++;
  1. x++erhöht xund gibt seinen alten Wert zurück.
  2. x = weist sich den alten Wert wieder zu.

Wird also am Ende xwieder seinem Anfangswert zugewiesen.

Mystisch
quelle
3
Was wirst du dann über x = ++ x sagen?
Hisham Muneer
3
@HishamMuneer xwird zuerst inkrementiert, bevor es in diesem Fall gelesen wird, sodass Sie am Ende mit x + 1.
@HishamMuneer Es ist zu spät. Aber ich setze es hier ein, weil es für einige andere Leute hilfreich sein kann, die in Zukunft schauen werden. Der beste Weg, um dieses Problem zu verstehen, ist der Assembler-Code, der für x = x ++ und x = ++ x erstellt wurde. Bitte beachten Sie auch die Antwort von Thinkingcap.
Nantitv
Ich weiß, das ist super alt, aber ich habe eine Frage. Ist die obige Betriebsreihenfolge durch die Norm garantiert? Ist es möglich, dass die Zuordnung vor dem Inkrement ausgeführt wird?
Smaragdwaffe
@EmeraldWeapon Es ist in Java definiert. Nur in C / C ++ sehen Sie diese Art von Spielereien.
Mysticial
385
x = x++;

ist äquivalent zu

int tmp = x;
x++;
x = tmp;
Prinz John Wesley
quelle
46
Lol, yay für rekursive Definitionen. Sie hätten es wahrscheinlich tun sollen, x=x+1anstattx++
user606723
8
@ user606723: Nein. Ich meinte die ganze Anweisung x = x++, nicht nur das Post-Inkrement x++.
Prinz John Wesley
20
Ich denke nicht, dass dies ohne weitere Erklärung so nützlich ist. Zum Beispiel ist es nicht wahr, dass dies x = ++x;auch äquivalent int tmp = x; ++x; x = tmp;ist. Aus welcher Logik können wir also schließen, dass Ihre Antwort richtig ist (welche es ist)?
KVB
4
noch deutlicher ist es in asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker
3
@forker: Ich denke, es wäre klarer, wenn Sie Montageanleitungen verwenden würden, die für den Prozessor gelten, den Michael verwendet;)
Carl
258

Die Aussage:

x = x++;

ist äquivalent zu:

tmp = x;   // ... this is capturing the value of "x++"
x = x + 1; // ... this is the effect of the increment operation in "x++" which
           //     happens after the value is captured.
x = tmp;   // ... this is the effect of assignment operation which is
           //     (unfortunately) clobbering the incremented value.

Kurz gesagt, die Aussage hat keine Wirkung.

Die wichtigsten Punkte:

  • Der Wert eines Postfix-Inkrement- / Dekrement-Ausdrucks ist der Wert des Operanden vor dem Inkrementieren / Dekrementieren. (Im Fall eines Präfixformulars ist der Wert der Wert des Operanden nach der Operation.)

  • Die RHS eines Zuweisungsausdrucks wird vollständig ausgewertet (einschließlich etwaiger Inkremente, Dekremente und / oder anderer Nebenwirkungen), bevor der Wert der LHS zugewiesen wird.

Beachten Sie, dass im Gegensatz zu C und C ++ die Reihenfolge der Auswertung eines Ausdrucks in Java vollständig angegeben ist und kein Platz für plattformspezifische Variationen vorhanden ist. Compiler dürfen die Vorgänge nur neu anordnen, wenn dies das Ergebnis der Ausführung des Codes aus der Perspektive des aktuellen Threads nicht ändert. In diesem Fall kann ein Compiler die gesamte Anweisung optimieren, da nachgewiesen werden kann, dass es sich um ein No-Op handelt.


Falls es nicht schon offensichtlich ist:

  • "x = x ++;" ist mit ziemlicher Sicherheit ein Fehler in jedem Programm.
  • Das OP (für die ursprüngliche Frage!) Bedeutete wahrscheinlich "x ++;" eher als "x = x ++;".
  • Aussagen , die auto inc / Abnahme- und Zuordnung auf denselben Variablen sind schwer zu verstehen, und kombinieren daher sollen vermieden werden , unabhängig von ihrer Richtigkeit . Es ist einfach nicht nötig, solchen Code zu schreiben.

Hoffentlich markieren Codeprüfer wie FindBugs und PMD Code wie diesen als verdächtig.

Stephen C.
quelle
7
Als Randnotiz, OP, wollen Sie wahrscheinlich nur sagen x++statt x = x++.
Jon Newmuis
3
Richtig, aber vielleicht betonen Sie, dass das Inkrement nach der Auswertung des rechten Ausdrucks erfolgt, aber vor der Zuordnung zur linken Seite, daher das offensichtliche "Überschreiben"
Bohemian
2
das scheint einer dieser High-School-Programmierer zu sein ... gut, um Ihre Grundlagen zu klären!
Kumarshar
1
@ Alberto - Es ist gut zu hören, dass Sie "Experten" -Statements nicht als "Evangeliumswahrheit" betrachten. Ein besserer Weg, um das zu bestätigen, was ich gesagt habe, wäre jedoch, das JLS zu konsultieren. Ihr Kompilierungs- / Dekompilierungstest zeigt nur, dass das, was ich gesagt habe, für einen Java-Compiler gültig ist. Andere könnten sich (hypothetisch) anders verhalten ... außer dass das JLS das nicht zulässt.
Stephen C
4
Nur zu Ihrer Information: Dies wurde ursprünglich in einer anderen Frage veröffentlicht, die als Duplikat dieser Frage geschlossen wurde und nun zusammengeführt wurde.
Shog9
33
int x = 7;
x = x++;

Es hat undefiniertes Verhalten in C und für Java siehe diese Antwort . Es hängt vom Compiler ab, was passiert.

user712092
quelle
4
Nein, es hängt nicht vom Compiler ab, gemäß der Antwort, die Sie zitiert haben - bitte bearbeiten Sie - -1 für jetzt
Mr_and_Mrs_D
@Mr_and_Mrs_D Dann kommt es darauf an was?
Mac
2
Es ist nur für C_ undefiniertes Verhalten. Trotzdem ist es irreführend zu sagen, dass es vom Compiler abhängt - dies impliziert, dass der Compiler dieses Verhalten irgendwie spezifizieren sollte. Ich stelle meine Stimme zurück, erwäge aber, Ihre Antwort zu bearbeiten - bearbeiten: Hoppla, ich kann nicht - Sie müssen sie zuerst bearbeiten: D
Mr_and_Mrs_D
16

Ein Konstrukt wie x = x++;zeigt an, dass Sie wahrscheinlich falsch verstehen, was der ++Operator tut:

// original code
int x = 7;
x = x++;

Schreiben wir dies neu, um dasselbe zu tun, basierend auf dem Entfernen des ++Operators:

// behaves the same as the original code
int x = 7;
int tmp = x; // value of tmp here is 7
x = x + 1; // x temporarily equals 8 (this is the evaluation of ++)
x = tmp; // oops! we overwrote y with 7

Lassen Sie es uns jetzt umschreiben, um (was ich denke) zu tun, was Sie wollten:

// original code
int x = 7;
x++;

Die Subtilität hier ist, dass der ++Operator die Variable ändertx , im Gegensatz zu einem Ausdruck wie x + x, der einen int-Wert ergibt, aber die Variable xselbst unverändert lässt. Betrachten Sie ein Konstrukt wie die ehrwürdige forSchleife:

for(int i = 0; i < 10; i++)
{
    System.out.println(i);
}

Beachten Sie die i++dort? Es ist der gleiche Operator. Wir könnten diese forSchleife so umschreiben und sie würde sich genauso verhalten:

for(int i = 0; i < 10; i = i + 1)
{
    System.out.println(i);
}

Ich empfehle ++in den meisten Fällen auch, den Operator nicht in größeren Ausdrücken zu verwenden. Wegen der Subtilität , wenn es die ursprüngliche Variable modifiziert in prä- gegen Post-Inkrement ( ++xund x++ist), ist es sehr einfach , subtile Bugs einzuführen, die nur schwer aufzuspüren.

FMM
quelle
13

Gemäß dem aus den Klassendateien erhaltenen Bytecode,

Beide Zuweisungen erhöhen x, aber die Differenz ist das Timing von when the value is pushed onto the stack

In Case1wird Push vor dem Inkrement ausgeführt (und später zugewiesen) (was im Wesentlichen bedeutet, dass Ihr Inkrement nichts bewirkt).

In Case2tritt Inkrement zuerst auf (macht es 8) und wird dann auf den Stapel geschoben (und dann x zugewiesen)

Fall 1:

int x=7;
x=x++;

Bytecode:

0  bipush 7     //Push 7 onto  stack
2  istore_1 [x] //Pop  7 and store in x
3  iload_1  [x] //Push 7 onto stack
4  iinc 1 1 [x] //Increment x by 1 (x=8)
7  istore_1 [x] //Pop 7 and store in x
8  return       //x now has 7

Fall 2:

int x=7; 
x=++x;

Bytecode

0  bipush 7     //Push 7 onto stack
2  istore_1 [x] //Pop 7 and store in x
3  iinc 1 1 [x] //Increment x by 1 (x=8)
6  iload_1  [x] //Push x onto stack
7  istore_1 [x] //Pop 8 and store in x
8  return       //x now has 8
  • Stapel bezieht sich hier auf Operandenstapel, lokal: x Index: 1 Typ: int
Evenprime
quelle
Können Sie bitte Ihre Antwort im Detail erklären.
Nihar
Bitte werfen Sie einen Blick auf den Link und Kommentare, auf die verwiesen wird
Evenprime
8

Es wird nach " x = x++;" erhöht . Es wäre 8, wenn Sie " x = ++x;" tun würden .

FJ
quelle
4
Wenn es danach erhöht wird x = x++, sollte es 8 sein.
R. Martinho Fernandes
8

Der Post-Increment-Operator funktioniert wie folgt:

  1. Speichern Sie den vorherigen Wert des Operanden.
  2. Erhöhen Sie den Wert des Operanden.
  3. Gibt den vorherigen Wert des Operanden zurück.

Also die Aussage

int x = 7;
x = x++; 

würde wie folgt bewertet werden:

  1. x wird mit dem Wert 7 initialisiert
  2. Der Post-Inkrement-Operator speichert den vorherigen Wert von x, dh 7, um zurückzukehren.
  3. Inkrementiert das x, also ist x jetzt 8
  4. Gibt den vorherigen Wert von x zurück, dh 7, und er wird wieder x zugewiesen, sodass x wieder zu 7 wird

X wird zwar erhöht, aber da x ++ x das Ergebnis zurückweist, wird der Wert von x auf den vorherigen Wert überschrieben.

GauravLuthra
quelle
Aber in msvc ist x 8. Ja in gcc und clang x ist 7.
Summer Sun
7

Das Inkrementieren erfolgt nach dem Aufruf von x, sodass x immer noch gleich 7 ist. ++ x wäre gleich 8, wenn x aufgerufen wird

JonPM
quelle
7

Wenn Sie den Wert erneut zuweisen x, ist er immer noch 7. Versuchen x = ++xSie es und Sie erhalten 8 weitere Aufgaben

x++; // don't re-assign, just increment
System.out.println(x); // prints 8
Vishal
quelle
6

weil x ++ den Wert erhöht, nachdem er der Variablen zugewiesen wurde. so weiter und während der Ausführung dieser Zeile:

x++;

Die Varialbe x hat weiterhin den ursprünglichen Wert (7), verwendet jedoch x erneut in einer anderen Zeile, z

System.out.println(x + "");

wird dir 8 geben.

Wenn Sie einen inkrementierten Wert von x für Ihre Zuweisungsanweisung verwenden möchten, verwenden Sie

++x;

Dadurch wird x um 1 erhöht, und dann wird der Variable x dieser Wert zugewiesen.

[Bearbeiten] anstelle von x = x ++ ist es nur x ++; Ersteres weist sich den ursprünglichen Wert von x zu, sodass in dieser Zeile eigentlich nichts unternommen wird.

Josephus
quelle
Derjenige, der sagt, dass er nach dem Zuweisen inkrementiert wird, und derjenige, der sagt, dass er 8 druckt. Er erhöht sich vor dem Zuweisen und druckt 7.
R. Martinho Fernandes
Wenn x ursprünglich 7 ist, System.out.println (String.valueOf (x ++)); druckt 7. Sind Sie sicher, dass es sich um dieselbe Programmiersprache handelt?
Josephus
Ja bin ich. Diese ideone.com/kj2UU druckt keine 8, wie diese Antwort behauptet.
R. Martinho Fernandes
Ja, ich habe mich geirrt. x = x ++ weist x zuerst 7 zu, bevor x erhöht wird. Da x ++ (was eine Zuweisung an sich ist) zuerst aufgelöst wird, bevor x = (was auch immer), folgt der Wert, der x in x = (was auch immer) zugewiesen wird. Entschuldigung, das habe ich nicht gesehen.
Josephus
1
Tatsächlich ist das Inkrement das erste, was passiert. ideone.com/xOIDU
R. Martinho Fernandes
4

Was passiert wann int x = 7; x = x++;?

ans -> x++bedeutet, dass Sie zuerst den Wert von x für den Ausdruck verwenden und ihn dann um 1 erhöhen.
Dies geschieht in Ihrem Fall. Der Wert von x bei RHS wird in die Variable x bei LHS kopiert und dann der Wert vonx um 1 erhöht.

In ähnlicher Weise ++x bedeutet ->, den Wert von x zuerst um eins zu erhöhen und dann im Ausdruck zu verwenden.
Wenn Sie dies tun, erhalten x = ++x ; // where x = 7
Sie in Ihrem Fall den Wert 8.

Versuchen Sie zur besseren Übersicht herauszufinden, wie viele printf-Anweisungen den folgenden Code ausführen

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend
Samprat
quelle
nicht korrekt "Der Wert von x auf RHS wird in die Variable x auf LHS kopiert und dann der Wert von x um 1 erhöht" - dies würde x8
bedeuten
3

++xDas Vorinkrement ->x wird vor der Verwendung
x++inkrementiert. Das Nachinkrement ->x wird nach der Verwendung inkrementiert

int x = 7; -> x get 7 value <br>
x = x++; -> x get x value AND only then x is incremented
Roberto Martelloni
quelle
1

Das heißt also: x++ist nicht gleichx = x+1

weil:

int x = 7; x = x++;
x is 7

int x = 7; x = x = x+1;
x is 8

und jetzt scheint es ein bisschen seltsam:

int x = 7; x = x+=1;
x is 8

sehr compilerabhängig!

Linuxeasy
quelle
2
Wer hat gesagt, dass es an erster Stelle gleich ist?
Fortan
1
Wenn ich Sie wäre, würde ich diese Bücher sofort in den Papierkorb werfen. XD In jedem Fall wäre es wie (x = x + 1, x-1)in C, wo durch Kommas getrennte Ausdrücke zulässig sind.
Fortan
3
@fortran: Nun, in meiner zehn Jahre alten Ausgabe von "The Java Programming Language, Third Edition" auf Seite 159 heißt es "" Der Ausdruck i ++ entspricht i = i + 1, außer dass i nur einmal ausgewertet wird ". Wer hat das gesagt? James Gosling, so scheint es. Dieser Teil dieser Ausgabe der Java-Spezifikation ist außerordentlich vage und schlecht spezifiziert. Ich gehe davon aus, dass spätere Ausgaben die Sprache bereinigt haben, um die tatsächliche Operatorsemantik klarer auszudrücken.
Eric Lippert
2
@fortran: Mit "außer i wird nur einmal ausgewertet" versucht der Standard zu vermitteln, dass ein Ausdruck wie "M (). x ++" M () nur einmal aufruft. Eine weniger vage und genauere Formulierung würde betonen, dass es einen Unterschied zwischen der Bewertung von i als Variable zur Bestimmung ihres Speicherorts gibt - was hier mit "nur einmal ausgewertet" gemeint ist - und dem Lesen oder Schreiben an diesen Speicherort - - Beides könnte eine vernünftige, aber falsche Interpretation von „bewertet“ sein. Natürlich muss der Speicherort sowohl gelesen als auch geschrieben werden!
Eric Lippert
1
"sehr compilerabhängig" - Überhaupt nicht!
Stephen C
-1

x = x ++;

Dies ist der Post-Inkrement-Operator. Es sollte verstanden werden als "Verwenden Sie den Wert des Operanden und erhöhen Sie dann den Operanden".

Wenn Sie möchten, dass das Gegenteil der Fall ist, dh "Inkrementieren Sie den Operanden und verwenden Sie dann den Wert des Operanden", müssen Sie den Operator vor dem Inkrementieren wie unten gezeigt verwenden.

x = ++ x;

Dieser Operator erhöht zuerst den Wert von x um 1 und weist den Wert dann wieder x zu.

deepak
quelle
-1

Ich denke, diese Kontroverse kann gelöst werden, ohne auf Code einzugehen und nur nachzudenken.

Betrachten Sie i ++ & ++ i als Funktionen, z. B. Func1 & Func2.

Jetzt ist i = 7;
Func1 (i ++) gibt 7 zurück, Func2 (++ i) gibt 8 zurück (jeder weiß das). Intern erhöhen beide Funktionen i auf 8, geben jedoch unterschiedliche Werte zurück.

Also ruft i = i ++ die Funktion Func1 auf. Innerhalb der Funktion erhöht sich i auf 8, aber nach Abschluss gibt die Funktion 7 zurück.

Also wird letztendlich 7 i zugewiesen. (Also am Ende ist i = 7)

Pranav Mahajan
quelle
2
Hier gibt es keine gültige "Kontroverse". Der Code verhält sich nachweislich auf eine bestimmte Art und Weise, und das Verhalten entspricht dem JLS. Wer denkt, dass es sich anders verhält, hat es entweder nicht ausprobiert oder wird getäuscht. (Dies ist ein bisschen wie zu sagen, dass 7 x 7 49 ist "umstritten", wenn jemand seine Stundenpläne vergessen hat ...)
Stephen C
-2

Dies liegt daran, dass Sie einen Post-Inkrement-Operator verwendet haben. In dieser folgenden Codezeile

x = x++;

Was passiert ist, dass Sie x den Wert von x zuweisen. x ++ erhöht x, nachdem der Wert von x x zugewiesen wurde. So funktionieren Operatoren nach dem Inkrementieren. Sie funktionieren, nachdem eine Anweisung ausgeführt wurde. In Ihrem Code wird x also zuerst zurückgegeben und anschließend inkrementiert.

Wenn du. .. getan hast

x = ++x;

Die Antwort wäre 8, da Sie den Operator vor dem Inkrementieren verwendet haben. Dies erhöht zuerst den Wert, bevor der Wert von x zurückgegeben wird.

Anon
quelle