Hinweis : Ursprünglich habe ich den C#-Code in dieser Antwort zur Veranschaulichung gepostet, da C# die Übergabe von int
Parameter durch Referenz mit der ref
Stichwort. Ich habe mich entschlossen, ihn mit aktuellem, legalem Java-Code zu aktualisieren, der das erste MutableInt
Klasse, die ich bei Google gefunden habe, um eine Art Annäherung an das ref
in C# tut. Ich kann nicht wirklich sagen, ob das hilft oder schadet die Antwort. Ich werde sagen, dass ich persönlich nicht getan haben, dass alle viel Java-Entwicklung; so für alles, was ich weiß, es könnte viel mehr idiomatische Möglichkeiten, um diesen Punkt zu veranschaulichen sein.
Vielleicht sollten wir eine Methode entwickeln, die dem entspricht, was x++
wird dies deutlicher machen.
public MutableInt postIncrement(MutableInt x) {
int valueBeforeIncrement = x.intValue();
x.add(1);
return new MutableInt(valueBeforeIncrement);
}
Oder? Den übergebenen Wert inkrementieren und den ursprünglichen Wert zurückgeben: Das ist die Definition des Postincrement-Operators.
Schauen wir uns nun an, wie sich dieses Verhalten in Ihrem Beispielcode auswirkt:
MutableInt x = new MutableInt();
x = postIncrement(x);
postIncrement(x)
macht was? Erhöht x
ja. Und dann gibt zurück, was x
war vor dem Inkrement . Dieser Rückgabewert wird dann zugewiesen an x
.
Daher ist die Reihenfolge der Werte, die den x
ist 0, dann 1, dann 0.
Dies wird vielleicht noch deutlicher, wenn wir den obigen Text umformulieren:
MutableInt x = new MutableInt(); // x is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
x = temp; // Now x is 0 again.
Ihre Fixierung auf die Tatsache, dass Sie, wenn Sie die x
auf der linken Seite der obigen Zuordnung mit y
Die Aussage "Sie können sehen, dass er zunächst x erhöht und später y zuordnet" scheint mir verwirrend. Es ist nicht x
die zugewiesen wird y
; es ist der Wert, der zuvor für x
. Wirklich, die Injektion y
unterscheidet sich nicht von dem obigen Szenario; wir haben einfach etwas:
MutableInt x = new MutableInt(); // x is 0.
MutableInt y = new MutableInt(); // y is 0.
MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0.
y = temp; // y is still 0.
Es ist also klar: x = x++
ändert den Wert von x nicht. Es bewirkt, dass x immer die Werte x 0 , dann x 0 + 1, und dann x 0 wieder.
Update : Übrigens, damit Sie keine Zweifel haben, dass x
jemals "zwischen" der Inkrement-Operation und der Zuweisung im obigen Beispiel auf 1 gesetzt wird, habe ich eine schnelle Demo zusammengestellt, um zu zeigen, dass dieser Zwischenwert tatsächlich "existiert", obwohl er im ausführenden Thread nie "gesehen" wird.
Die Demo ruft auf x = x++;
in einer Schleife, während ein separater Thread kontinuierlich den Wert von x
an die Konsole.
public class Main {
public static volatile int x = 0;
public static void main(String[] args) {
LoopingThread t = new LoopingThread();
System.out.println("Starting background thread...");
t.start();
while (true) {
x = x++;
}
}
}
class LoopingThread extends Thread {
public @Override void run() {
while (true) {
System.out.println(Main.x);
}
}
}
Nachfolgend sehen Sie einen Auszug aus der Ausgabe des obigen Programms. Beachten Sie das unregelmäßige Auftreten von 1en und 0en.
Starting background thread...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1
8 Stimmen
Ich vermute, dass folgendes passiert: 1. x in ein Register laden (=0); 2. x inkrementieren (x=1); 3. Registerwert in x speichern (x=0). In C/C++ wäre dies ein undefiniertes Verhalten, da es keinen formalen Sequenzpunkt gibt, der die Reihenfolge von 2 und 3 definiert. Hoffentlich kann Ihnen jemand etwas Entsprechendes aus der Java-Spezifikation zitieren.
1 Stimmen
Das ist wirklich interessant, denn auch wenn die
x
ist zugeordnet zux
zuerst, dann sollte die Inkrementierung gut funktionieren.19 Stimmen
Wir haben dies in C++ ausprobiert, um zu sehen, was passieren würde, und es gibt 1,2,3 aus und beendet sich. Das hatte ich nicht erwartet. Ich nehme an, dass es compilerabhängig ist, da es ein undefiniertes Verhalten ist. Wir haben den gnu g++ verwendet.
1 Stimmen
@grieve Ich sehe das gleiche hier in C, wirklich seltsam das unterschiedliche Verhalten.
1 Stimmen
Das größere Ziel wurde erreicht: 1000 Reputationen (jetzt kann ich die Summe der Auf- und Abwärtsstimmen sehen) =D
1 Stimmen
Bevor jemand wütend wird, das mit dem Ruf war nur ein Scherz ;)
1 Stimmen
@Tom Brito: +1, weil es wirklich cool ist, zu sehen, dass ein so einfaches Stück Code zu 67+ Stimmen führt (68 jetzt mit meiner). Ich hoffe, das wird die +100 Stimmen überschreiten :) Und die 22+ Favoriten. Respekt :)
1 Stimmen
@Webinator, es ist jetzt einen Schritt näher. Das ist eine großartige Frage, eine der wenigen, die ich auf SO gesehen habe, die sich tatsächlich wie ein gültiger Grund für die Existenz von SO anfühlt... =) und >2k Views in 14 Stunden ist ziemlich beeindruckend.
1 Stimmen
Wir hatten jemanden, der dies in unserem Code (C++) getan hat. Bei einem Compiler funktionierte es wie vorgesehen, bei einem anderen Compiler ging es einfach nicht. Ich denke auch, wenn Sie sich auf den Pre- oder Post-Teil von Increment verlassen, sollten Sie Ihren Code ändern.
1 Stimmen
@Tom Brito: hallo Ihre Erklärung ist gut.. ich habe einen Zweifel.. ich habe die Codierung getestet; der Gedanke ist Zeile 1: x=x++;, Zeile 2:Console.Writeline(x); nach dieser Anweisung vor der Ausführung von Zeile 2 muss Zeile 1 abgeschlossen sein, bin ich richtig? In Zeile 1 wird der Wert von x zugewiesen oder inkrementiert. Wenn der erste Wert von x zugewiesen wird, dann muss er inkrementiert werden, bevor Zeile 2 ausgeführt wird... mein Zweifel ist also, wie Null auf allen Wegen gedruckt wird... meine getestete Antwort ist Null
14 Stimmen
@saj
x++
ist nach der Inkrementierung;x=
ist die Zuordnung von résultat die résultat dex++
ist das Originalx
(und es gibt einen Nebeneffekt des Inkrementierens, aber das ändert nichts am Ergebnis), also kann dies interpretiert werden alsvar tmp = x; x++; x = tmp;
1 Stimmen
+1 für eine klare Darstellung einer Frage, die in der Regel dazu führt, dass Anwälte mit der Sprache kämpfen. Übrigens hat mein Upvote die Gesamtzahl auf 100 erhöht...
0 Stimmen
Es ist sehr seltsam, dass das Verhalten in der Sprache C anders ist. Vielleicht wird in C zuerst der Wert gelesen und dann später erhöht, während in Java erst erhöht und dann der alte Wert zurückgegeben wird.
0 Stimmen
@Tom: Soweit ich weiß, ist dieses Verhalten in C undefiniert, so dass unterschiedliche Implementierungen ein unterschiedliches Verhalten aufweisen können. Aber in Java ist dieses Verhalten definiert, wie hier beschrieben: stackoverflow.com/questions/3834005/
0 Stimmen
Eine Sache, die man sich bei C merken sollte, ist, dass es weitgehend eine Ad-hoc-Sprache war. Sie wurde erst in den 1980er Jahren standardisiert, und wie Dennis Ritchie in cm.bell-labs.com/cm/cs/who/dmr/chist.html kann selbst der AT&T-Compiler nicht als "Referenz"-Implementierung angesehen werden. Es ist wahrscheinlich, dass es mehrere unabhängige Implementierungen von Post-Increment gab, so dass der Standard sich zu diesem Thema ausschweigen musste. Im Vergleich dazu wurde die Java-Sprache als einheitliches Ganzes entwickelt.
0 Stimmen
@saj Ich denke, das Update von Dan Tao beantwortet diese Frage
5 Stimmen
Jetzt, wo ich eine beliebte Frage habe, bedaure ich, dass die (wiederholten) Antworten nicht aufhören, auch wenn die richtige Antwort gewählt wurde. Mein Bildschirm "Kürzliche Aktivitäten" ist voll von denselben Antworten, und es kommen immer mehr...
0 Stimmen
Wenn Sie stattdessen ++x verwendet hätten, wäre alles in Ordnung gewesen.
3 Stimmen
@Rob Vermeulen Sie sollten vielleicht die ganze Frage lesen, bevor Sie einen Kommentar abgeben ;) Dies war ein Code, der von einem Studenten von mir gemacht wurde, und ich war neugierig, warum dieses Verhalten.
0 Stimmen
Eine Sache, die in Eclipse irgendwie lustig ist, ist, dass man in Eclipse keine Warnungen erhält, wenn man seinen Code testet, der die Endlosschleife erzeugt. Wechseln Sie jedoch zu
++x;
was tatsächlich funktioniert und das gewünschte Ergebnis liefert, und Eclipse gibt eine Warnung aus, dassthe assignment to variable x has no effect
. Meiner Meinung nach hätte es diese Fehlermeldung an denx++
Beispiel.0 Stimmen
Ich habe diese Frage schon ein paar Mal gestellt und wollte einfach mal Hallo sagen! Deine gewählte Antwort war auch für mich illusorisch, ich habe nie daran gedacht, x++; als Abkürzung für eine Methode zu sehen, die das Gleiche tut, und ich hatte die falsche Vorstellung, dass x++ bedeutet, dass x inkrementiert wird, nachdem die aktuelle Zeile/der aktuelle Ausdruck beendet ist, aber das ist unmöglich, weil man ein return oldVal; und dann etwas danach hätte, was wegen des Returns unerreichbarer Code ist. Der Kreis schließt sich wieder!
0 Stimmen
Eine saubere Antwort: coderanch.com/t/659942/Wiki/Post-Increment-Operator-Zuweisung
0 Stimmen
"Bei x = x++ sollte x zunächst sich selbst zugeschrieben und später inkrementiert werden" - ich kann nicht verstehen, warum jemand dies für wahr hält (und erst recht nicht in Frage stellt). In jedem anderen Fall von foo = Ausdruck wird foo der Wert des Ausdrucks zugewiesen; der Ausdruck wird nicht in Teile zerlegt, sondern ein Teil wird der Variablen zugewiesen und dann die Ausführung des anderen Teils. Angenommen, wir hätten
int x; /* global variable */ x = function_that_does_stuff_to_x()
. Sicherlich erwarten wir, dass die Dinge, die mit X geschehen, auch geschehen. vor wird ihm der Wert der Funktion zugewiesen. Java formalisiert dies. C tut das nicht.