J'ai le code suivant:
public class Tests {
public static void main(String[] args) throws Exception {
int x = 0;
while(x<3) {
x = x++;
System.out.println(x);
}
}
}
Nous savons qu'il aurait dû écrire juste x++
ou x=x+1
, mais x = x++
il devrait d'abord s'attribuer x
à lui-même, puis l'incrémenter. Pourquoi x
continue avec 0
comme valeur?
--mise à jour
Voici le bytecode:
public class Tests extends java.lang.Object{
public Tests();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Exception;
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_3
4: if_icmpge 22
7: iload_1
8: iinc 1, 1
11: istore_1
12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
15: iload_1
16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
19: goto 2
22: return
}
Je vais lire les instructions pour essayer de comprendre ...
x++
est post-incrément;x=
est l'affectation du résultat ; le résultat dex++
est l'originalx
(et il y a un effet secondaire d'incrémentation, mais cela ne change pas le résultat), donc cela peut être interprété commevar tmp = x; x++; x = tmp;
Réponses:
Remarque : à l'origine, j'ai publié du code C # dans cette réponse à des fins d'illustration, car C # vous permet de passer des
int
paramètres par référence avec leref
mot clé. J'ai décidé de le mettre à jour avec le code Java légal réel en utilisant la premièreMutableInt
classe que j'ai trouvée sur Google pour trier approximativement ce qui seref
passe en C #. Je ne peux pas vraiment dire si cela aide ou nuit à la réponse. Je dirai que personnellement, je n'ai pas fait beaucoup de développement Java; donc pour tout ce que je sais, il pourrait y avoir des façons beaucoup plus idiomatiques pour illustrer ce point.Peut-être que si nous écrivons une méthode pour faire l'équivalent de ce
x++
que cela fera, cela sera plus clair.Droite? Incrémentez la valeur passée et retournez la valeur d'origine: c'est la définition de l'opérateur post-incrément.
Voyons maintenant comment se comporte ce comportement dans votre exemple de code:
postIncrement(x)
Fait quoi? Incrémentsx
, oui. Et puis retourne ce quix
était avant l'incrément . Cette valeur de retour est ensuite affectée àx
.Ainsi, l'ordre des valeurs attribuées à
x
est 0, puis 1, puis 0.Cela pourrait être encore plus clair si nous réécrivons ce qui précède:
Votre fixation sur le fait que lorsque vous remplacez
x
à gauche de l'affectation ci-dessus pary
", vous pouvez voir qu'elle incrémente d'abord x, puis l'attribue à y" me semble confuse. Ce n'est pas cex
qui est assigné ày
; il s'agit de la valeur précédemment attribuéex
. Vraiment, l'injection ney
fait rien de différent du scénario ci-dessus; nous avons simplement:Donc, c'est clair:
x = x++
effectivement ne change pas la valeur de x. Il fait toujours en sorte que x ait les valeurs x 0 , puis x 0 + 1, puis x 0 nouveau .Mise à jour : Incidemment, de peur que vous doutiez que
x
jamais soit attribué à 1 "entre" l'opération d'incrémentation et l'affectation dans l'exemple ci-dessus, j'ai rassemblé une démo rapide pour illustrer que cette valeur intermédiaire "existe" bien, bien qu'elle ne jamais être "vu" sur le fil d'exécution.La démo appelle
x = x++;
en boucle tandis qu'un thread séparé imprime en continu la valeur dex
à la console.Ci-dessous est un extrait de la sortie du programme ci-dessus. Remarquez l'occurrence irrégulière des 1 et des 0.
la source
Integer
classe, qui fait partie de la bibliothèque standard, et elle a même l'avantage d'être mise en boîte automatique vers et depuis de manièreint
presque transparente.x
dans votre dernier exemple doit être déclarévolatile
, sinon c'est un comportement non défini et voir1
s est spécifique à l'implémentation.++
pourrait être fait avant ou après l'affectation. En pratique, il pourrait y avoir un compilateur qui fait la même chose que Java, mais vous ne voudriez pas parier dessus.x = x++
fonctionne de la manière suivante:x++
. L'évaluation de cette expression produit une valeur d'expression (qui est la valeur d'x
avant l'incrément) et des incrémentsx
.x
, écrasant la valeur incrémentée.Ainsi, la séquence d'événements ressemble à ceci (c'est un bytecode décompilé réel, tel que produit par
javap -c
, avec mes commentaires):À titre de comparaison,
x = ++x
:la source
iinc
incrémentation d'une variable, elle n'incrémente pas une valeur de pile, ni ne laisse de valeur sur la pile (contrairement à presque toutes les autres opérations arithmétiques). Vous voudrez peut-être ajouter le code généré par++x
pour comparaison.Cela se produit car la valeur de
x
n'est pas du tout incrémentée.est équivalent à
Explication:
Regardons le code d'octet pour cette opération. Prenons un exemple de classe:
Maintenant, en exécutant le désassembleur de classe, nous obtenons:
Maintenant, la machine virtuelle Java est basée sur la pile, ce qui signifie que pour chaque opération, les données seront poussées sur la pile et à partir de la pile, les données apparaîtront pour effectuer l'opération. Il existe également une autre structure de données, généralement un tableau pour stocker les variables locales. Les variables locales reçoivent des identifiants qui ne sont que les index du tableau.
Regardons les mnémoniques dans la
main()
méthode:iconst_0
: La valeur constante0
est poussée sur la pile.istore_1
: L'élément supérieur de la pile est sorti et stocké dans la variable locale avec index1
qui est
x
.iload_1
: La valeur à l'emplacement1
qui est la valeurx
est0
, est poussée dans la pile.iinc 1, 1
: La valeur à l'emplacement mémoire1
est incrémentée de1
. Alorsx
devient maintenant1
.istore_1
: La valeur en haut de la pile est stockée dans l'emplacement mémoire1
. Cela est0
affecté à l'x
écrasement de sa valeur incrémentée.Par conséquent, la valeur de
x
ne change pas, ce qui entraîne la boucle infinie.la source
++
), mais la variable est remplacée plus tard.int temp = x; x = x + 1; x = temp;
il vaut mieux ne pas utiliser de tautologie dans votre exemple.Cependant, "
=
" a une priorité d'opérateur inférieure à "++
".Il
x=x++;
faut donc évaluer comme suitx
préparé pour l'affectation (évalué)x
incrémentéx
affecté àx
.la source
++
a une priorité plus élevée qu'en=
C et C ++, mais l'instruction n'est pas définie dans ces langages.Aucune des réponses n'était assez précise, alors voici:
Lorsque vous écrivez
int x = x++
, vous n'affectez pasx
à être lui-même à la nouvelle valeur, vous affectezx
à être la valeur de retour de l'x++
expression. Ce qui se trouve être la valeur d'origine dex
, comme l'indique la réponse de Colin Cochrane .Pour le plaisir, testez le code suivant:
Le résultat sera
La valeur de retour de l'expression est la valeur initiale de
x
, qui est zéro. Mais plus tard, lors de la lecture de la valeur dex
, nous recevons la valeur mise à jour, c'est-à-dire une.la source
Il a déjà été bien expliqué par d'autres. J'inclus simplement les liens vers les sections de spécifications Java pertinentes.
x = x ++ est une expression. Java suivra l' ordre d'évaluation . Il évaluera d'abord l'expression x ++, qui incrémentera x et définira la valeur de résultat à la valeur précédente de x . Ensuite, il affectera le résultat de l' expression à la variable x. À la fin, x revient à sa valeur précédente.
la source
Cette déclaration:
évalue comme ceci:
x
sur la pile;x
;x
de la pile.La valeur est donc inchangée. Comparez cela à:
qui évalue comme:
x
;x
sur la pile;x
de la pile.Ce que vous voulez c'est:
la source
x++
extrait de code.x++;
votre solutionx=x; x++;
et vous faites ce que vous prétendez que le code d'origine fait.La réponse est assez simple. Cela a à voir avec l'ordre dans lequel les choses sont évaluées.
x++
renvoie la valeurx
puis incrémentex
.Par conséquent, la valeur de l'expression
x++
est0
. Vous attribuez donc àx=0
chaque fois dans la boucle.x++
Incrémente certainement cette valeur, mais cela se produit avant l'affectation.la source
Depuis http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html
Pour illustrer, essayez ce qui suit:
Qui imprimera 1 et 0.
la source
Vous obtenez effectivement le comportement suivant.
L'idée étant que l'opérateur de post-incrémentation (x ++) incrémente cette variable en question APRÈS avoir renvoyé sa valeur à utiliser dans l'équation dans laquelle elle est utilisée.
Edit: Ajout un peu à cause du commentaire. Considérez-le comme suit.
la source
Vous n'avez pas vraiment besoin du code machine pour comprendre ce qui se passe.
Selon les définitions:
L'opérateur d'affectation évalue l'expression de droite et la stocke dans une variable temporaire.
1.1. La valeur actuelle de x est copiée dans cette variable temporaire
1.2. x est incrémenté maintenant.
La variable temporaire est ensuite copiée dans le côté gauche de l'expression, qui est x par hasard! C'est pourquoi l'ancienne valeur de x est à nouveau copiée en elle-même.
C'est assez simple.
la source
C'est parce qu'il n'est jamais incrémenté dans ce cas.
x++
va en utiliser la valeur avant d'incrémenter comme dans ce cas ce sera comme:Mais si vous le faites,
++x;
cela augmentera.la source
La valeur reste à 0 car la valeur de
x++
est 0. Dans ce cas, peu importe que la valeur dex
soit augmentée ou non, l'affectationx=0
est exécutée. Cela écrasera la valeur incrémentée temporaire dex
(qui était 1 pendant "un temps très court").la source
x++
, pas pour l'ensemble du devoirx=x++;
Cela fonctionne comme vous vous attendez de l'autre. C'est la différence entre le préfixe et le postfix.
la source
Considérez x ++ comme un appel de fonction qui "renvoie" ce qu'était X avant l'incrémentation (c'est pourquoi il est appelé post-incrémentation).
Ainsi, l'ordre d'opération est le suivant:
1: mettre en cache la valeur de x avant l'incrémentation
2: incrémenter x
3: renvoyer la valeur mise en cache (x avant qu'elle ne soit incrémentée)
4: la valeur de retour est affectée à x
la source
Lorsque le ++ est sur le rhs, le résultat est renvoyé avant que le nombre ne soit incrémenté. Changez en ++ x et ça aurait été bien. Java aurait optimisé cela pour effectuer une seule opération (l'affectation de x à x) plutôt que l'incrément.
la source
Pour autant que je puisse voir, l'erreur se produit, en raison de l'affectation remplaçant la valeur incrémentée, avec la valeur avant l'incrémentation, c'est-à-dire qu'elle annule l'incrément.
Plus précisément, l'expression "x ++" a la valeur de "x" avant l'incrémentation par opposition à "++ x" qui a la valeur de "x" après l'incrémentation.
Si vous êtes intéressé à enquêter sur le bytecode, nous allons jeter un œil aux trois lignes en question:
7: iload_1 # mettra la valeur de la 2ème variable locale sur la pile
8: iinc 1,1 # incrémentera la 2ème variable locale avec 1, notez qu'elle laisse la pile intacte!
9: istore_1 # fera apparaître le haut de la pile et enregistrera la valeur de cet élément dans la 2e variable locale
(vous pouvez lire les effets de chaque instruction JVM ici )
C'est pourquoi le code ci-dessus bouclera indéfiniment, contrairement à la version avec ++ x. Le bytecode pour ++ x devrait être assez différent, si je me souviens bien du compilateur Java 1.3 que j'ai écrit il y a un peu plus d'un an, le bytecode devrait ressembler à ceci:
Il suffit donc d'échanger les deux premières lignes pour modifier la sémantique de sorte que la valeur laissée en haut de la pile, après l'incrément (c'est-à-dire la «valeur» de l'expression) soit la valeur après l'incrément.
la source
Donc:
Tandis que
Donc:
Bien sûr, le résultat final est le même que juste
x++;
ou++x;
sur une ligne par lui-même.la source
à cause de l'énoncé ci-dessus, x n'atteint jamais 3;
la source
Je me demande s'il y a quelque chose dans la spécification Java qui définit précisément le comportement de cela. (L'implication évidente de cette déclaration étant que je suis trop paresseux pour vérifier.)
Notez d'après le bytecode de Tom, les lignes clés sont 7, 8 et 11. La ligne 7 charge x dans la pile de calcul. Incréments de la ligne 8 x. La ligne 11 stocke la valeur de la pile dans x. Dans des cas normaux où vous ne vous attribuez pas de valeurs, je ne pense pas qu'il y ait une raison pour laquelle vous ne pourriez pas charger, stocker, puis incrémenter. Vous obtiendriez le même résultat.
Par exemple, supposons que vous ayez eu un cas plus normal où vous avez écrit quelque chose comme: z = (x ++) + (y ++);
Que ce soit dit (pseudocode pour ignorer les détails techniques)
ou
devrait être hors de propos. L'une ou l'autre implémentation devrait être valide, je pense.
Je serais extrêmement prudent lors de l'écriture de code qui dépend de ce comportement. Cela me semble très dépendant de l'implémentation, entre les fissures dans les spécifications. La seule fois où cela ferait une différence, c'est si vous avez fait quelque chose de fou, comme l'exemple ici, ou si vous aviez deux threads en cours d'exécution et dépendiez de l'ordre d'évaluation dans l'expression.
la source
Je pense que parce qu'en Java ++ a une priorité plus élevée que = (affectation) ... Est-ce? Regardez http://www.cs.uwf.edu/~eelsheik/cop2253/resources/op_precedence.html ...
De la même manière, si vous écrivez x = x + 1 ... + a une priorité plus élevée que = (affectation)
la source
++
a également une priorité plus élevée qu'en=
C et C ++, mais l'instruction n'est pas définie.L'
x++
expression est évaluée àx
. La++
partie affecte la valeur après l' évaluation , pas après l' instruction .x = x++
est donc effectivement traduit enla source
Avant d'incrémenter la valeur de un, la valeur est affectée à la variable.
la source
Cela se produit parce qu'il est incrémenté. Cela signifie que la variable est incrémentée après l'évaluation de l'expression.
x est maintenant 10, mais y est 9, la valeur de x avant son incrémentation.
Voir plus dans Définition de Post Incrément .
la source
x
/y
exemple est différent du vrai code, et la différence est pertinente. Votre lien ne mentionne même pas Java. Pour deux des langues , il ne CITATION la déclaration dans la question est non défini.Vérifiez le code ci-dessous,
la sortie sera,
post increment
signifie incrémenter la valeur et renvoyer la valeur avant l'incrément . C'est pourquoi la valeurtemp
est0
. Alors que faire sitemp = i
et cela est dans une boucle (sauf pour la première ligne de code). comme dans la question !!!!la source
L'opérateur d'incrémentation est appliqué à la même variable que celle à laquelle vous attribuez. C'est demander des ennuis. Je suis sûr que vous pouvez voir la valeur de votre variable x lors de l'exécution de ce programme .... cela devrait expliquer pourquoi la boucle ne se termine jamais.
la source