a = (a ++) * (a ++) donne des résultats étranges en Java

J’étudie pour l’examen OCPJP et je dois donc comprendre chaque petit détail étrange de Java. Cela inclut l’ordre dans lequel les opérateurs avant et après l’incrémentation s’appliquent aux variables. Le code suivant me donne des résultats étranges:

int a = 3; a = (a++) * (a++); System.out.println(a); // 12 

La réponse ne devrait pas être 11? Ou peut-être 13? Mais pas 12!

SUIVRE:

Quel est le résultat du code suivant?

 int a = 3; a += (a++) * (a++); System.out.println(a); 

    Après le premier a++ a devient 4. Vous avez donc 3 * 4 = 12 .

    ( a devient 5 après le 2nd a++ , mais cela est ignoré, car l’affectation a = remplace)

    Votre déclaration:

     a += (a++) * (a++); 

    est équivalent à l’un de ceux-ci:

     a = a*a + 2*a a = a*(a+2) a += a*(a+1) 

    Utilisez l’un de ceux-ci à la place.

    a++ signifie «la valeur de a, et a est alors incrémenté de 1». Donc, quand vous courez

     (a++) * (a++) 

    le premier a++ est évalué en premier et produit la valeur 3. a est ensuite incrémenté de 1. Le second a++ est alors évalué. a produit la valeur 4 et est ensuite incrémenté à nouveau (mais cela n’a pas d’importance maintenant)

    Donc, cela se transforme en

     a = 3 * 4 

    qui est égal à 12.

     int a = 3; a += (a++) * (a++); 

    Construisez d’abord l’arbre de syntaxe:

     += a * a++ a++ 

    Pour l’évaluer, commencez par l’élément le plus externe et descendez récursivement. Pour chaque élément faire:

    • Évaluer les enfants de gauche à droite
    • Evaluez l’élément lui-même

    L’opérateur += est spécial: il est étendu à quelque chose comme left = left + right , mais seulement en évaluant l’expression left une fois. Encore le côté gauche est évalué à une valeur (et pas seulement une variable) avant que le côté droit soit évalué à une valeur.

    Cela mène à:

    1. Commencez à évaluer +=
    2. Évaluer le côté gauche de l’affectation à la variable a .
    3. Évaluez la variable a à la valeur 3 qui sera utilisée dans l’addition.
    4. Commencez à évaluer *
    5. Evaluez le premier a++ . Cela renvoie la valeur actuelle de 3 et définit a à 4
    6. Evaluez le second a++ . Cela retourne la valeur actuelle d’un 4 et définit a à 5
    7. Calculez le produit: 3 * 4 = 12
    8. Exécuter += . Le côté gauche a été évalué à 3 dans la troisième étape et le côté droit à 12 . Donc, il affecte 3 + 12 = 15 à a .
    9. La valeur finale de a est 15.

    Une chose à noter ici est que la priorité des opérateurs n’a aucune influence directe sur l’ordre d’évaluation. Cela n’affecte que la forme de l’arbre et donc indirectement l’ordre. Mais parmi les frères et sœurs de l’arbre, l’évaluation est toujours de gauche à droite, quelle que soit la priorité de l’opérateur.

    (a++) est un post-incrément, donc la valeur de l’expression est 3.

    (a++) est post-incrémentation, donc la valeur de l’expression est maintenant 4.

    L’évaluation de l’expression se passe de gauche à droite.

     3 * 4 = 12 

    Chaque fois que vous utilisez un ++, vous effectuez une post-incrémentation a. Cela signifie que le premier a ++ est évalué à 3 et le second à 4. 3 * 4 = 12.

    Il y a un manque général de compréhension sur le fonctionnement des opérateurs. Honnêtement, chaque opérateur est du sucre syntaxique.

    Tout ce que vous avez à faire est de comprendre ce qui se passe réellement derrière chaque opérateur. Supposons ce qui suit:

     a = b -> Operators.set(a, b) //don't forget this returns b a + b -> Operators.add(a, b) a - b -> Operators.subtract(a, b) a * b -> Operators.multiply(a, b) a / b -> Operators.divide(a, b) 

    Les opérateurs composés peuvent alors être réécrits en utilisant ces généralisations (veuillez ignorer les types de retour dans un souci de simplicité):

     Operators.addTo(a, b) { //a += b return Operators.set(a, Operators.add(a, b)); } Operators.preIncrement(a) { //++a return Operators.addTo(a, 1); } Operators.postIncrement(a) { //a++ Operators.set(b, a); Operators.addTo(a, 1); return b; } 

    Vous pouvez réécrire votre exemple:

     int a = 3; a = (a++) * (a++); 

    comme

     Operators.set(a, 3) Operators.set(a, Operators.multiply(Operators.postIncrement(a), Operators.postIncrement(a))); 

    Qui peut être divisé en utilisant plusieurs variables:

     Operators.set(a, 3) Operators.set(b, Operators.postIncrement(a)) Operators.set(c, Operators.postIncrement(a)) Operators.set(a, Operators.multiply(b, c)) 

    C’est certainement plus verbeux de cette façon, mais il devient immédiatement évident que vous ne voulez jamais effectuer plus de deux opérations sur une seule ligne.

    En cas de :

     int a = 3; a = (a++) * (a++); a = 3 * a++; now a is 4 because of post increment a = 3 * 4; now a is 5 because of second post increment a = 12; value of 5 is overwritten with 3*4 ie 12 

    par conséquent, nous obtenons une sortie de 12.

    En cas de :

     a += (a++) * (a++); a = a + (a++) * (a++); a = 3 + (a++) * (a++); // a is 3 a = 3 + 3 * (a++); //a is 4 a = 3 + 3 * 4; //a is 5 a = 15 

    Le point principal à noter ici est que dans ce cas, le compilateur résout de gauche à droite et, en cas d’incrémentation, la valeur avant l’incrémentation est utilisée dans le calcul et lorsque nous nous déplaçons de gauche à droite, la valeur incrémentée est utilisée.

    (a++) signifie renvoyer a et a incrément, donc (a++) * (a++) signifie 3 * 4

    Voici le code java:

     int a = 3; a = (a++)*(a++); 

    Voici le bytecode:

      0 iconst_3 1 istore_1 [a] 2 iload_1 [a] 3 iinc 1 1 [a] 6 iload_1 [a] 7 iinc 1 1 [a] 10 imul 11 istore_1 [a] 

    Voici ce qui se passe:

    Pousse 3 dans la stack, puis en sort 3 et la stocke dans un. Maintenant a = 3 et la stack est vide.

      0 iconst_3 1 istore_1 a 

    Maintenant, il pousse la valeur de “a” (3) dans la stack, puis incrémente a (3 -> 4).

      2 iload_1 [a] 3 iinc 1 1 [a] 

    Donc maintenant “a” est égal à “4” la stack est égale à {3}.

    Ensuite, il charge à nouveau “a” (4), pousse dans la stack et incrémente “a”.

      6 iload_1 [a] 7 iinc 1 1 [a] 

    Maintenant, “a” est égal à 5 ​​et la stack est égale à {4,3}

    Donc, il place enfin les deux premières valeurs de la stack (4 et 3), les multiplie et les stocke dans la stack (12).

      10 imul 

    Maintenant, “a” est égal à 5 ​​et la stack est égale à 12.

    Enfin, il y a des pops 12 de la stack et des magasins a.

      11 istore_1 [a] 

    TADA!

    Il est 12. L’expression commence à évaluer de gauche. Donc ça fait:

     a = (3++) * (4++); 

    Une fois que la première partie (3 ++) est évaluée, a est 4, donc dans la partie suivante, elle fait un = 3 * 4 = 12. Notez que le dernier post-incrémentation (4 ++) est exécuté mais n’a aucun effet car a est assigné la valeur 12 après cela.

    Exemple 1

     int a = 3; a = (++a) * (a++); System.out.println(a); // 16 

    Exemple 2

     int a = 3; a = (++a) * (++a); System.out.println(a); // 20 

    Juste pour vous assurer où mettre ++ expression qui change la valeur en fonction de l’emplacement.

    Tout le monde a clairement expliqué la première expression, et pourquoi la valeur de a est 12.

    Pour le suivi sur la question, la réponse est totalement évidente pour l’observateur occasionnel:

    17

    Les incréments pré et post-préfixe ont une priorité plus élevée que l’opérateur de multiplication. par conséquent, l’expression est évaluée à 3 * 4.

    Si vous utilisez un ++ la prochaine fois que vous utilisez un, il est incrémenté de un. Donc tu fais

     a = 3 * (3 + 1) = 3 * 4 = 12