Pourquoi cela entre-t-il dans une boucle infinie?


493

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 xcontinue avec 0comme 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 ...


8
Je soupçonne que ce qui se passe est: 1. charge x dans un registre (= 0); 2. incrémenter x (x = 1); 3. enregistrez la valeur du registre dans x (x = 0). En C / C ++, ce serait un comportement indéfini car il n'y a pas de point de séquence formel pour définir l'ordre de 2 et 3. J'espère que quelqu'un peut vous citer quelque chose d'équivalent à partir de la spécification Java.
Rup

19
Nous avons essayé cela en C ++ pour voir ce qui se passerait, et il affiche 1,2,3 et se ferme. Je ne m'attendais pas à ça. Je suppose que cela dépend du compilateur, car c'est un comportement indéfini. Nous avons utilisé le gnu g ++.
pleurer le

13
@saj x++est post-incrément; x=est l'affectation du résultat ; le résultat de x++est l'original x(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;
Marc Gravell

5
Maintenant que j'ai une question populaire, je le regrette, les réponses (répétées) n'arrêtent pas de venir, même après que la bonne réponse ait été choisie. Mon écran "Activité récente" regorge des mêmes réponses, et à venir ...
Tom Brito

3
@Rob Vermeulen vous voudrez peut-être lire la question complète avant de faire un commentaire ..;) C'était un code fait par un de mes étudiants, et j'étais curieux de savoir pourquoi de ce comportement.
Tom Brito

Réponses:


354

Remarque : à l'origine, j'ai publié du code C # dans cette réponse à des fins d'illustration, car C # vous permet de passer des intparamè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ère MutableIntclasse que j'ai trouvée sur Google pour trier approximativement ce qui se refpasse 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.

public MutableInt postIncrement(MutableInt x) {
    int valueBeforeIncrement = x.intValue();
    x.add(1);
    return new MutableInt(valueBeforeIncrement);
}

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:

MutableInt x = new MutableInt();
x = postIncrement(x);

postIncrement(x)Fait quoi? Incréments x, oui. Et puis retourne ce qui x était avant l'incrément . Cette valeur de retour est ensuite affectée à x.

Ainsi, l'ordre des valeurs attribuées à xest 0, puis 1, puis 0.

Cela pourrait être encore plus clair si nous réécrivons ce qui précède:

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.

Votre fixation sur le fait que lorsque vous remplacez xà gauche de l'affectation ci-dessus par y", vous pouvez voir qu'elle incrémente d'abord x, puis l'attribue à y" me semble confuse. Ce n'est pas ce xqui est assigné à y; il s'agit de la valeur précédemment attribuéex . Vraiment, l'injection ne yfait rien de différent du scénario ci-dessus; nous avons simplement:

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.

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 xjamais 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 de xà la console.

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);
        }
    }
}

Ci-dessous est un extrait de la sortie du programme ci-dessus. Remarquez l'occurrence irrégulière des 1 et des 0.

Démarrage du thread d'arrière-plan ...
0
0
1
1
0
0
0
0
0
0
0
0
0
0
1
0
1

1
Vous n'avez pas besoin de créer une classe pour passer par référence en java (bien que cela fonctionnerait certainement). Vous pouvez utiliser la Integerclasse, 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ère int presque transparente.
rmeador

3
@rmeador Integer est immuable, vous ne pouvez donc toujours pas changer sa valeur. AtomicInteger, cependant, est modifiable.
ILMTitan

5
@Dan: Soit dit en passant, xdans votre dernier exemple doit être déclaré volatile, sinon c'est un comportement non défini et voir 1s est spécifique à l'implémentation.
axtavt

4
@burkestar: Je ne pense pas que ce lien soit tout à fait approprié dans ce cas, car c'est une question Java et (sauf erreur de ma part), le comportement n'est en fait pas défini en C ++.
Dan Tao

5
@Tom Brito - en C ce n'est pas défini ... le ++ 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.
detly

170

x = x++ fonctionne de la manière suivante:

  • Il évalue d'abord l'expression x++. L'évaluation de cette expression produit une valeur d'expression (qui est la valeur d' xavant l'incrément) et des incréments x.
  • Plus tard, il affecte la valeur d'expression à 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):

   8: iload_1 // Mémorise la valeur actuelle de x dans la pile
   9: iinc 1, 1 // Incrémenter x (ne change pas la pile)
   12: istore_1 // Ecrit la valeur mémorisée de la pile dans x

À titre de comparaison, x = ++x:

   8: iinc 1, 1 // Incrément x
   11: iload_1 // Poussez la valeur de x sur la pile
   12: istore_1 // Pop valeur de la pile à x

si vous effectuez un test, vous pouvez voir qu'il s'incrémente d'abord et les attributs ultérieurs. Il ne doit donc pas attribuer zéro.
Tom Brito

2
@Tom c'est le but, cependant - parce que tout cela n'est qu'une séquence, il fait les choses dans un ordre non évident (et probablement indéfini). En essayant de tester cela, vous ajoutez un point de séquence et obtenez un comportement différent.
Rup

Concernant votre sortie de bytecode: notez que l' iincincré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 ++xpour comparaison.
Anon

3
@Rep Il peut ne pas être défini en C ou C ++, mais en Java, il est bien défini.
ILMTitan


104

Cela se produit car la valeur de xn'est pas du tout incrémentée.

x = x++;

est équivalent à

int temp = x;
x++;
x = temp;

Explication:

Regardons le code d'octet pour cette opération. Prenons un exemple de classe:

class test {
    public static void main(String[] args) {
        int i=0;
        i=i++;
    }
}

Maintenant, en exécutant le désassembleur de classe, nous obtenons:

$ javap -c test
Compiled from "test.java"
class test extends java.lang.Object{
test();
  Code:
   0:    aload_0
   1:    invokespecial    #1; //Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   0:    iconst_0
   1:    istore_1
   2:    iload_1
   3:    iinc    1, 1
   6:    istore_1
   7:    return
}

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 constante 0 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 index 1
    qui est x.
  • iload_1: La valeur à l'emplacement 1qui est la valeur x est 0, est poussée dans la pile.
  • iinc 1, 1: La valeur à l'emplacement mémoire 1est incrémentée de 1. Alors xdevient maintenant 1.
  • istore_1: La valeur en haut de la pile est stockée dans l'emplacement mémoire 1. Cela est 0affecté à l' x écrasement de sa valeur incrémentée.

Par conséquent, la valeur de xne change pas, ce qui entraîne la boucle infinie.


5
En fait, il est incrémenté (c'est la signification de ++), mais la variable est remplacée plus tard.
Progman

10
int temp = x; x = x + 1; x = temp;il vaut mieux ne pas utiliser de tautologie dans votre exemple.
Scott Chamberlain

52
  1. La notation de préfixe incrémentera la variable AVANT que l'expression ne soit évaluée.
  2. La notation de suffixe augmentera APRÈS l'évaluation de l'expression.

Cependant, " =" a une priorité d'opérateur inférieure à " ++".

Il x=x++;faut donc évaluer comme suit

  1. x préparé pour l'affectation (évalué)
  2. x incrémenté
  3. Valeur précédente de xaffecté à x.

C'est la meilleure réponse. Un balisage l'aurait aidé à se démarquer un peu plus.
Justin Force

1
C'est faux. Il ne s'agit pas de priorité. ++a une priorité plus élevée qu'en =C et C ++, mais l'instruction n'est pas définie dans ces langages.
Matthew Flaschen

2
La question d'origine concerne Java
Jaydee

34

Aucune des réponses n'était assez précise, alors voici:

Lorsque vous écrivez int x = x++, vous n'affectez pas xà être lui-même à la nouvelle valeur, vous affectez xà être la valeur de retour de l' x++expression. Ce qui se trouve être la valeur d'origine de x, comme l'indique la réponse de Colin Cochrane .

Pour le plaisir, testez le code suivant:

public class Autoincrement {
        public static void main(String[] args) {
                int x = 0;
                System.out.println(x++);
                System.out.println(x);
        }
}

Le résultat sera

0
1

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 de x, nous recevons la valeur mise à jour, c'est-à-dire une.


Je vais essayer de comprendre les lignes de bytecode, voir ma mise à jour, donc ce sera clair .. :)
Tom Brito

L'utilisation de println () m'a été très utile pour comprendre cela.
ErikE

29

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.


1
+1. C'est de loin la meilleure réponse à la vraie question, "Pourquoi?"
Matthew Flaschen

18

Cette déclaration:

x = x++;

évalue comme ceci:

  1. Pousser xsur la pile;
  2. Incrément x;
  3. Pop xde la pile.

La valeur est donc inchangée. Comparez cela à:

x = ++x;

qui évalue comme:

  1. Incrément x;
  2. Poussez xsur la pile;
  3. Pop xde la pile.

Ce que vous voulez c'est:

while (x < 3) {
  x++;
  System.out.println(x);
}

13
Certainement la bonne mise en œuvre, mais la question est «pourquoi?».
p.campbell

1
Le code d'origine utilisait la post-incrémentation sur x, puis l'attribuait à x. x sera lié à x avant l'incrémentation, il ne modifiera donc jamais les valeurs.
wkl

5
@cletus Je ne suis pas le downvoter, mais votre réponse initiale ne contenait pas l'explication. Il a juste dit de faire 'x ++ `.
Petar Minchev

4
@cletus: Je n'ai pas downvote, mais votre réponse à l'origine était juste l' x++extrait de code.
p.campbell

10
L'explication est également incorrecte. Si le code affectait d'abord x à x, puis incrémentait x, cela fonctionnerait bien. Modifiez simplement x++;votre solution x=x; x++;et vous faites ce que vous prétendez que le code d'origine fait.
Wooble

10

La réponse est assez simple. Cela a à voir avec l'ordre dans lequel les choses sont évaluées. x++renvoie la valeur xpuis incrémentex .

Par conséquent, la valeur de l'expression x++est 0. Vous attribuez donc à x=0chaque fois dans la boucle. x++Incrémente certainement cette valeur, mais cela se produit avant l'affectation.


1
Wow, il y a tellement de détails sur cette page lorsque la réponse est courte et simple, c'est-à-dire celle-ci.
Charles Goodwin

8

Depuis http://download.oracle.com/javase/tutorial/java/nutsandbolts/op1.html

Les opérateurs d'incrémentation / décrémentation peuvent être appliqués avant (préfixe) ou après (postfixe) l'opérande. Le code result ++; et ++ résultat; se terminera tous les deux par un incrément d'un résultat. La seule différence est que la version du préfixe (résultat ++) est évaluée à la valeur incrémentée, tandis que la version postfixée (résultat ++) est évaluée à la valeur d'origine . Si vous effectuez simplement une augmentation / diminution simple, la version que vous choisissez n'a pas vraiment d'importance. Mais si vous utilisez cet opérateur dans une partie d'une expression plus large, celle que vous choisissez peut faire une différence significative.

Pour illustrer, essayez ce qui suit:

    int x = 0;
    int y = 0;
    y = x++;
    System.out.println(x);
    System.out.println(y);

Qui imprimera 1 et 0.


1
Ce n'est pas le résultat de l'évaluation qui est en cause, cependant, c'est l'ordre des magasins.
Rup

2
Je ne suis pas d'accord. Si x = 0, alors x ++ renverra 0. Par conséquent, x = x ++ se traduira par x = 0.
Colin Cochrane

Rup a raison à ce sujet. C'est l'ordre des magasins qui est en cause dans ce cas particulier. y = x ++ n'est pas identique à x = x ++; Sur ce dernier, x reçoit 2 valeurs dans la même expression. À gauche x est affecté le résultat de l' évaluation de l'expression x ++, qui est 0. Le côté droit x est incrémenté à 1. Dans quel ordre ces 2 affectations se produisent est le problème. D'après les articles précédents, il est clair que la façon dont cela fonctionne est la suivante: eval = x ++ => eval == 0: incrémentation droite x => x == 1: gauche x = eval => x == 0
Michael Ekoka

7

Vous obtenez effectivement le comportement suivant.

  1. saisir la valeur de x (qui est 0) comme "le résultat" du côté droit
  2. incrémenter la valeur de x (donc x est maintenant 1)
  3. affecter le résultat du côté droit (qui a été enregistré sous 0) à x (x est maintenant 0)

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.

x = 1;        // x == 1
x = x++ * 5;
              // First, the right hand side of the equation is evaluated.
  ==>  x = 1 * 5;    
              // x == 2 at this point, as it "gave" the equation its value of 1
              // and then gets incremented by 1 to 2.
  ==>  x = 5;
              // And then that RightHandSide value is assigned to 
              // the LeftHandSide variable, leaving x with the value of 5.

OK, mais qu'est-ce qui spécifie l'ordre des étapes 2 et 3?
Rup

@Rup - Le langage le définit. Le côté droit de l'équation est évalué en premier (dans ce cas, "x ++"), et le résultat est affecté à la variable du côté gauche. Voilà comment fonctionne la langue. En ce qui concerne le "x ++" "renvoyant" x pour l'équation, c'est ainsi que fonctionne l'incrémentation postfixée (renvoyer la valeur de x, puis l'incrémenter). S'il avait été "--x", il l'aurait été (incrémentez x, puis retournez la valeur). Le retour n'est pas le bon mot, mais vous avez l'idée.
RHSeeger

7

Vous n'avez pas vraiment besoin du code machine pour comprendre ce qui se passe.

Selon les définitions:

  1. 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.

  2. 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.


5

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:

x = 0;

Mais si vous le faites, ++x;cela augmentera.


si vous effectuez un test, vous pouvez voir qu'il s'incrémente d'abord et les attributs ultérieurs. Il ne doit donc pas attribuer zéro.
Tom Brito

1
@Tom: voir ma réponse - je montre dans un test que x ++ retourne en fait l'ancienne valeur de x. C'est là que ça casse.
Robert Munteanu

"si vous faites un test" - certaines personnes semblent penser qu'un test écrit en C nous dit ce que Java fera, alors qu'il ne nous dira même pas ce que C fera.
Jim Balter

3

La valeur reste à 0 car la valeur de x++est 0. Dans ce cas, peu importe que la valeur de xsoit augmentée ou non, l'affectation x=0est exécutée. Cela écrasera la valeur incrémentée temporaire de x(qui était 1 pendant "un temps très court").


Mais x ++ est une post-opération. Il faudrait donc incrémenter x une fois l'affectation terminée.
Sagar V

1
@Sagar V: uniquement pour l'expression x++, pas pour l'ensemble du devoirx=x++;
Progman

Non, je pense qu'il ne doit être incrémenté qu'après la lecture de la valeur de x à utiliser dans l'affectation.
Rup

1

Cela fonctionne comme vous vous attendez de l'autre. C'est la différence entre le préfixe et le postfix.

int x = 0; 
while (x < 3)    x = (++x);

1

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


OK, mais qu'est-ce qui spécifie l'ordre des étapes 3 et 4?
Rup

"retourne ce qu'était X avant l'incrément" est faux, voir ma mise à jour
Tom Brito

En réalité, les étapes 3 et 4 ne sont pas des opérations distinctes - ce n'est pas vraiment un appel de fonction qui renvoie une valeur, cela aide simplement à y penser de cette façon. Chaque fois que vous avez une affectation, le côté droit est "évalué", puis le résultat est affecté à la gauche, le résultat de l'évaluation peut être considéré comme une valeur de retour car il vous aide à comprendre l'ordre des opérations, mais ce n'est pas vraiment .
jhabbott

Oups, c'est vrai. Je voulais dire les étapes 2 et 4 - pourquoi la valeur retournée est-elle stockée par-dessus la valeur incrémentée?
Rup

1
Cela fait partie de la définition d'une opération d'affectation, d'abord le côté droit est complètement évalué, puis le résultat est affecté au côté gauche.
jhabbott

1

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.


1

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
 8:   iinc    1, 1
11:  istore_1

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:

iinc 1,1
iload_1
istore_1

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.


1
    x++
=: (x = x + 1) - 1

Donc:

   x = x++;
=> x = ((x = x + 1) - 1)
=> x = ((x + 1) - 1)
=> x = x; // Doesn't modify x!

Tandis que

   ++x
=: x = x + 1

Donc:

   x = ++x;
=> x = (x = x + 1)
=> x = x + 1; // Increments x

Bien sûr, le résultat final est le même que juste x++;ou ++x;sur une ligne par lui-même.


0
 x = x++; (increment is overriden by = )

à cause de l'énoncé ci-dessus, x n'atteint jamais 3;


0

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)

load x
increment x
add y
increment y
store x+y to z

ou

load x
add y
store x+y to z
increment x
increment y

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.



0

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 en

int y = x; // evaluation
x = x + 1; // increment part
x = y; // assignment

0

Avant d'incrémenter la valeur de un, la valeur est affectée à la variable.


0

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.

int x = 9;
int y = x++;

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 .


1
Votre x/ yexemple 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.
Matthew Flaschen du

0

Vérifiez le code ci-dessous,

    int x=0;
    int temp=x++;
    System.out.println("temp = "+temp);
    x = temp;
    System.out.println("x = "+x);

la sortie sera,

temp = 0
x = 0

post incrementsignifie incrémenter la valeur et renvoyer la valeur avant l'incrément . C'est pourquoi la valeur tempest 0. Alors que faire si temp = iet cela est dans une boucle (sauf pour la première ligne de code). comme dans la question !!!!


-1

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.