Qu'est-ce que x après «x = x ++»?


285

Que se passe-t-il (derrière les rideaux) lorsque cela est exécuté?

int x = 7;
x = x++;

Autrement dit, lorsqu'une variable est post-incrémentée et affectée à elle-même dans une seule instruction? J'ai compilé et exécuté cela. xest toujours 7 même après la déclaration entière . Dans mon livre, il est dit que xc'est incrémenté!


9
Essayez ceci: int x = 7; x = ++x;bien sûr, le code est toujours horrible, vous n'avez pas besoin de le réaffecter. int x = 7; x++;est assez.
stivlo

7
C'est une très mauvaise pratique, n'incrémentez pas la variable dans la même ligne que vous l'utilisez.
Yousf

5
Je préfère utiliser x += 1, sauf peut-être en boucles. for(int x=0; x<7; x++)
Svish

2
@andyortlieb il n'y a pas d'objet, juste une valeur de base.
fortran

Réponses:


301

xest incrémenté. Mais vous attribuez l'ancienne valeur de xretour en soi.


x = x++;
  1. x++ incréments x et renvoie son ancienne valeur.
  2. x = assigne l'ancienne valeur à lui-même.

Donc à la fin, x revient à sa valeur initiale.


3
Alors, que direz-vous de x = ++ x;
Hisham Muneer

3
@HishamMuneer xest incrémenté en premier avant d'être lu dans ce cas, donc vous vous retrouvez avec x + 1.

@HishamMuneer Il est trop tard. Mais je le mets ici parce qu'il peut être utile pour d'autres personnes qui regarderont à l'avenir. La meilleure façon de comprendre ce problème consiste à examiner le code d'assembly créé pour x = x ++ et x = ++ x. Veuillez également consulter la réponse de Thinkingcap.
nantitv

Je sais que c'est super vieux, mais j'ai une question. L'ordre de fonctionnement ci-dessus est-il garanti par la norme? Est-il possible que l'affectation soit exécutée avant l'incrément?
Emerald Weapon

@EmeraldWeapon Il est défini en Java. Ce n'est qu'en C / C ++ que vous voyez ce genre de manigances.
Mysticial

385
x = x++;

est équivalent à

int tmp = x;
x++;
x = tmp;

46
Lol, yay pour les définitions récursives. vous auriez probablement dû faire à la x=x+1place dex++
user606723

8
@ user606723: Non. Je voulais dire toute la déclaration x = x++, pas seulement l'incrément de publication x++.
Prince John Wesley

20
Je ne pense pas que ce soit très utile sans autre explication. Par exemple, ce n'est pas vrai qui x = ++x;est également équivalent à int tmp = x; ++x; x = tmp;, alors par quelle logique peut-on en déduire que votre réponse est correcte (quelle est-elle)?
kvb

4
encore plus clair c'est dans asm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker

3
@forker: Je pense que ce serait plus clair si vous utilisiez des instructions d'assemblage qui s'appliquent au processeur que Michael utilise;)
Carl

258

La déclaration:

x = x++;

est équivalent à:

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.

Bref, la déclaration n'a aucun effet.

Les points clés:

  • La valeur d'une expression d'incrémentation / décrémentation de Postfix est la valeur de l'opérande avant que l'incrémentation / décrémentation n'ait lieu. (Dans le cas d'un formulaire Prefix, la valeur est la valeur de l'opérande après l'opération,)

  • le RHS d'une expression d'affectation est complètement évalué (y compris tous les incréments, décréments et / ou autres effets secondaires) avant que la valeur ne soit affectée au LHS.

Notez que contrairement à C et C ++, l'ordre d'évaluation d'une expression en Java est totalement spécifié et il n'y a pas de place pour une variation spécifique à la plate-forme. Les compilateurs ne sont autorisés à réorganiser les opérations que si cela ne change pas le résultat de l'exécution du code du point de vue du thread actuel. Dans ce cas, un compilateur serait autorisé à optimiser l'intégralité de l'instruction car il peut être prouvé qu'il s'agit d'un no-op.


Au cas où ce n'est pas déjà évident:

  • "x = x ++;" est presque certainement une erreur dans n'importe quel programme.
  • L'OP (pour la question d'origine!) Signifiait probablement "x ++;" plutôt que "x = x ++;".
  • Les instructions qui combinent auto inc / décrémentation et affectation sur la même variable sont difficiles à comprendre et doivent donc être évitées quelle que soit leur exactitude . Il n'est tout simplement pas nécessaire d'écrire du code comme ça.

Espérons que les vérificateurs de code comme FindBugs et PMD signaleront ce code comme suspect.


7
En remarque, OP, vous voulez probablement dire simplement x++au lieu de x = x++.
Jon Newmuis

3
Correct, mais peut-être insister sur le fait que l'incrémentation se produit après l' évaluation de l'expression à droite, mais pré- affectation à gauche, d'où l'apparente "écrasement"
Bohème

2
cela semble être l'un de ces virevoltes de programmation au lycée ... bon pour clarifier vos bases!
kumarharsh

1
@Alberto - Il est bon d'entendre que vous ne considérez pas les déclarations "d'experts" comme une "vérité évangélique". Cependant, une meilleure façon de valider ce que j'ai dit serait de consulter le JLS. Votre test de compilation / décompilation montre seulement que ce que j'ai dit est valable pour un seul compilateur Java. D'autres pourraient (hypothétiquement) se comporter différemment ... sauf que le JLS ne le permet pas.
Stephen C

4
Juste un info: cela a été initialement publié dans une autre question, qui a été fermée en double de celle-ci et a maintenant été fusionnée.
Shog9

33
int x = 7;
x = x++;

Il a un comportement indéfini en C et pour Java voir cette réponse . Cela dépend du compilateur ce qui se passe.


4
Non, cela ne dépend pas du compilateur selon la réponse que vous avez citée - veuillez modifier - -1 pour l'instant
Mr_and_Mrs_D

@Mr_and_Mrs_D Alors ça dépend de quoi?
Mac

2
C'est un comportement non défini_ uniquement pour C_. Même en disant que cela dépend du compilateur est trompeur - cela implique que le compilateur devrait en quelque sorte spécifier ce comportement. Je reviens sur mon vote, mais pensez à modifier votre réponse - modifier: oups je ne peux pas - vous devez d'abord le modifier: D
Mr_and_Mrs_D

16

Une construction comme x = x++;indique que vous comprenez probablement mal ce que fait l' ++opérateur:

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

Réécrivons ceci pour faire la même chose, basé sur la suppression de l' ++opérateur:

// 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

Maintenant, réécrivons-le pour faire (ce que je pense) que vous vouliez:

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

La subtilité ici est que l' ++opérateur modifie la variablex , contrairement à une expression telle que x + x, qui évaluerait une valeur int mais laisserait la variable xelle-même inchangée. Considérons une construction comme la forboucle vénérable :

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

Remarquez le i++dedans? C'est le même opérateur. Nous pourrions réécrire cette forboucle comme ceci et elle se comporterait de la même manière:

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

Je déconseille également d'utiliser l' ++opérateur dans des expressions plus grandes dans la plupart des cas. En raison de la subtilité du moment où il modifie la variable d'origine avant et après l'incrémentation ( ++xet x++respectivement), il est très facile d'introduire des bogues subtils difficiles à localiser.


13

Selon le code d'octet obtenu à partir des fichiers de classe,

Les deux affectations incrémentent x, mais la différence est le moment de when the value is pushed onto the stack

Dans Case1 , Push se produit (puis est ensuite attribué) avant l'incrément (ce qui signifie essentiellement que votre incrément ne fait rien)

Dans Case2, l'incrémentation se produit d'abord (ce qui en fait 8) puis poussé sur la pile (puis affecté à x)

Cas 1:

int x=7;
x=x++;

Code d'octet:

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

Cas 2:

int x=7; 
x=++x;

Code d'octet

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
  • La pile ici fait référence à la pile d'opérandes, locale: index x: 1 type: int

Pouvez-vous expliquer votre réponse en détail.
Nihar

Veuillez consulter le lien référencé et les commentaires
evenprime

8

Il est incrémenté après " x = x++;". Ce serait 8 si vous faisiez " x = ++x;".


4
S'il est incrémenté par la suite x = x++, il devrait être de 8.
R. Martinho Fernandes

8

L'opérateur Post Increment fonctionne comme suit:

  1. Stocke la valeur précédente de l'opérande.
  2. Incrémentez la valeur de l'opérande.
  3. Renvoie la valeur précédente de l'opérande.

Donc, la déclaration

int x = 7;
x = x++; 

serait évalué comme suit:

  1. x est initialisé avec la valeur 7
  2. L'opérateur post incrément stocke la valeur précédente de x, c'est-à-dire 7 à renvoyer.
  3. Incrémente le x, alors maintenant x est 8
  4. Renvoie la valeur précédente de x, c'est-à-dire 7 et elle est affectée à nouveau à x, donc x redevient 7

Donc x est en effet augmenté mais comme x ++ assigne le résultat à x, la valeur de x est remplacée par sa valeur précédente.


Mais dans msvc x est 8. Oui dans gcc et clang x est 7.
Summer Sun

7

L'incrémentation se produit après l'appel de x, donc x est toujours égal à 7. ++ x serait égal à 8 lorsque x est appelé


7

Lorsque vous réattribuez la valeur, xelle est toujours de 7. Essayez x = ++xet vous obtiendrez 8 autres choses

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

6

car x ++ incrémente la valeur APRÈS l'affectation à la variable. ainsi de suite et pendant l'exécution de cette ligne:

x++;

le varialbe x aura toujours la valeur d'origine (7), mais en utilisant à nouveau x sur une autre ligne, comme

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

vous donnera 8.

si vous souhaitez utiliser une valeur incrémentée de x dans votre instruction d'affectation, utilisez

++x;

Cela incrémentera x de 1, PUIS attribuez cette valeur à la variable x.

[Modifier] au lieu de x = x ++, c'est juste x ++; l'ancien attribue la valeur d'origine de x à lui-même, donc il ne fait rien sur cette ligne.


Celui qui dit qu'il incrémente après assignation, et celui qui dit qu'il imprimera 8. Il incrémente avant assignation, et il imprime 7.
R. Martinho Fernandes

si x est à l'origine 7, System.out.println (String.valueOf (x ++)); imprime 7. tu es sûr que nous parlons du même langage de programmation?
josephus

Oui. Cette ideone.com/kj2UU n'imprime pas 8, comme le prétend cette réponse.
R. Martinho Fernandes

oui, j'avais tort. x = x ++ affectera d'abord 7 à x avant d'incrémenter x. comme x ++ (qui est une affectation en soi) se résout d'abord avant x = (peu importe), la valeur assignée à x dans x = (peu importe) suivra. désolé je n'ai pas vu ça.
josephus

1
En fait, l'augmentation est la première chose qui se produit. ideone.com/xOIDU
R. Martinho Fernandes

4

Ce qu'il se passe quand int x = 7; x = x++; ?

ans -> x++signifie d'abord utiliser la valeur de x pour l'expression, puis l'augmenter de 1.
C'est ce qui se passe dans votre cas. La valeur de x sur RHS est copiée dans la variable x sur LHS, puis la valeur dex est augmentée de 1.

De la même ++x manière, ->augmenter d'abord la valeur de x de un, puis l'utiliser dans l'expression.
Donc, dans votre cas, si vous le faites, x = ++x ; // where x = 7
vous obtiendrez une valeur de 8.

Pour plus de clarté, essayez de savoir combien d'instructions printf exécuteront le code suivant

while(i++ <5)   
  printf("%d" , ++i);   // This might clear your concept upto  great extend

incorrect "La valeur de x sur RHS est copiée dans la variable x sur LHS, puis la valeur de x est augmentée de 1" - cela ferait x8, mais c'est 7 - l'incrément se produit entre la lecture et l'affectation
user85421

3

++xest pré-incrémenté ->x est incrémenté avant d' être utilisé
x++est post-incrémenté ->x est incrémenté après avoir été utilisé

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

1

Cela signifie donc: x++n'est pas égal àx = x+1

car:

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

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

et maintenant ça semble un peu étrange:

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

très dépendant du compilateur!


2
qui a dit que c'était égal en premier lieu?
fortran

1
Si j'étais vous, je jetterais ces livres immédiatement xD Dans tous les cas, ce serait comme (x = x + 1, x-1)en C, où les expressions séparées par des virgules sont autorisées.
fortran

3
@fortran: Eh bien, dans ma copie vieille de dix ans du "Langage de programmation Java, troisième édition" à la page 159, il est indiqué "" L'expression i ++ équivaut à i = i + 1, sauf que i n'est évalué qu'une seule fois ". Qui a dit James Gosling, semble-t-il. Cette partie de cette édition de la spécification Java est extraordinairement vague et mal spécifiée; je suppose que les éditions ultérieures ont nettoyé le langage pour exprimer plus clairement la sémantique réelle de l'opérateur.
Eric Lippert

2
@fortran: par "sauf que i n'est évalué qu'une seule fois", la norme tente de transmettre qu'une expression comme "M (). x ++" n'appelle M () qu'une seule fois. Une formulation moins vague et plus précise soulignerait qu'il y a une différence entre évaluer i en tant que variable pour déterminer son emplacement de stockage - ce que l'on entend ici par "évalué une seule fois" - et lire ou écrire sur cet emplacement de stockage - - l'une ou l'autre pourrait être une interprétation raisonnable mais incorrecte de «évalué». Il est clair que l'emplacement de stockage doit être à la fois lu et écrit!
Eric Lippert

1
"très dépendant du compilateur" - Pas du tout!
Stephen C

-1

x = x ++;

Il s'agit de l'opérateur post-incrémentation. Il doit être compris comme "Utilisez la valeur de l'opérande puis incrémentez l'opérande".

Si vous souhaitez que l'inverse se produise, c'est-à-dire "Incrémenter l'opérande puis utiliser la valeur de l'opérande", vous devez utiliser l'opérateur de pré-incrémentation comme indiqué ci-dessous.

x = ++ x;

Cet opérateur incrémente d'abord la valeur de x de 1, puis attribue la valeur à x.


-1

Je pense que cette controverse peut être résolue sans entrer dans le code et juste penser.

Considérez i ++ et ++ i comme des fonctions, par exemple Func1 et Func2.

Maintenant i = 7;
Func1 (i ++) renvoie 7, Func2 (++ i) renvoie 8 (tout le monde le sait). En interne, les deux fonctions incrémentent i à 8, mais elles renvoient des valeurs différentes.

Donc i = i ++ appelle la fonction Func1. À l'intérieur de la fonction, i augmente jusqu'à 8, mais une fois terminée, la fonction renvoie 7.

Donc, finalement, 7 est alloué à i. (Donc au final, i = 7)


2
Il n'y a pas de «controverse» valable ici. Le code se comporte manifestement d'une manière particulière et le comportement est conforme au JLS. Quiconque pense qu'il se comporte différemment ne l'a pas essayé ou est trompé. (C'est un peu comme dire que 7 x 7 est 49 est "controversé" quand quelqu'un a oublié ses horaires ...)
Stephen C

-2

En effet, vous avez utilisé un opérateur de post-incrémentation. Dans cette ligne de code suivante

x = x++;

Ce qui se passe, c'est que vous attribuez la valeur de x à x. x ++ incrémente x une fois que la valeur de x est affectée à x. C'est ainsi que fonctionnent les opérateurs post-incrémentation. Ils travaillent après l'exécution d'une déclaration. Ainsi, dans votre code, x est retourné d'abord après, puis il est ensuite incrémenté.

Si tu l'as fait

x = ++x;

La réponse serait 8 car vous avez utilisé l'opérateur de pré-incrémentation. Cela incrémente la valeur avant de renvoyer la valeur de x.

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.