「x = x ++」の後のxは何ですか?


285

これが実行されると(カーテンの後ろで)何が起こりますか?

int x = 7;
x = x++;

つまり、変数がポストインクリメントされ、1つのステートメントでそれ自体に割り当てられると、これをコンパイルして実行しました。ステートメント全体の後でさえ、xまだ7 です。私の本では、それはインクリメントされていると言っています!x


9
これを試してください:int x = 7; x = ++x;もちろん、それでも恐ろしいコードです。再割り当てする必要はありません。int x = 7; x++;十分です
stivlo、2011年

7
これは本当に悪い習慣です。使用するのと同じ行で変数をインクリメントしないでください。
Yousf、2011年

5
x += 1多分ループ内を除いて、私はを使用したいと思います。for(int x=0; x<7; x++)
Svish

2
@andyortliebオブジェクトはなく、基本的な値です。
Fortran、2011年

回答:


301

x増加します。しかし、あなたx自身の中に古い価値を割り当てています。


x = x++;
  1. x++x古い値をインクリメントして返します。
  2. x = 古い値をそれ自体に割り当てます。

つまり、最終的にxは、初期値に割り当てられます。


3
次に、x = ++ xについて何を言いますか。
Hisham Muneer、2014年

3
@HishamMuneer xは、その場合、読み取られる前に最初にインクリメントされるため、最終的にはになりますx + 1

@HishamMuneer手遅れです。しかし、将来見る他の一部の人にとって役立つかもしれないので、ここに入れます。この問題を理解する最良の方法は、x = x ++およびx = ++ x用に作成されたアセンブリコードを確認することです。Thinkingcapの回答もご覧ください。
nantitv 2015

これは非常に古いことはわかっていますが、質問があります。上記の動作順序は規格によって保証されていますか?インクリメントの前に割り当てが実行される可能性はありますか?
エメラルドの武器

@EmeraldWeapon Javaで定義されています。それはC / C ++だけにあります。
Mysticial

385
x = x++;

に相当

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

46
笑い、再帰的な定義に賛成です。あなたはおそらくx=x+1代わりに行うべきでしたx++
user606723

8
@ user606723:いいえ。x = x++投稿の増分だけでなく、ステートメント全体を意味しましたx++
ジョンウェスリー王子

20
これ以上の説明がなければ、これはそれほど役に立ちません。たとえば、とx = ++x;同じであるというのは真実ではないint tmp = x; ++x; x = tmp;ので、どのロジックであなたの答えが正しいと推測できますか?
kvb 2011年

4
さらに明確にasm x=x++ =MOV x,tmp; INC x; MOV tmp,x
forker

3
@forker:Michaelが使用しているプロセッサに適用されるアセンブリ命令を使用すると、より明確になると思います;)
Carl

258

ステートメント:

x = x++;

以下と同等です。

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.

つまり、このステートメントは効果がありません。

重要なポイント:

  • Postfixインクリメント/デクリメント式の値は、インクリメント/デクリメントが行われる前のオペランドの値です。(プレフィックス形式の場合、値は演算のオペランドの値です)

  • 値がLHSに割り当てられるに、割り当て式のRHSが完全に評価されます(増分、減分、および/またはその他の副作用を含む)。

CやC ++とは異なり、Javaでの式の評価順序は完全に指定されており、プラットフォーム固有のバリエーションの余地がないことに注意してください。コンパイラは、現在のスレッドの観点からコードを実行した結果を変更しない場合にのみ、操作を並べ替えることができます。この場合、ノーオペレーションであることを証明できるため、コンパイラーはステートメント全体を最適化することが許可されます。


まだ明確でない場合:

  • 「x = x ++;」ほとんどすべてのプログラムで間違いです。
  • OP(元の質問の場合!)はおそらく「x ++」を意味していました。「x = x ++;」ではなく。
  • auto inc / decrementと同じ変数への代入を組み合わせたステートメントは理解しにくいため、その正確性に関係なく回避する必要があります。そのようなコードを書く必要はまったくありません。

うまくいけば、FindBugsやPMDなどのコードチェッカーが、このようなコードに疑わしいフラグを立てます。


7
補足として、OPさん、おそらくのx++代わりに単に言うことを意味しますx = x++
Jon Newmuis

3
正しい、多分増加が起こることを強調ポスト右の式の評価を、しかし、事前に左側への割り当てを、したがって、見かけ上「上書き」
ボヘミアン

2
高校のプログラミングのツイスターのように思えます...基本を片付けるのに良いです!
クマールハーシュ

1
@アルベルト-「専門家の」発言を「福音の真実」として受け取らないと聞いて良かったです。ただし、私が言ったことを検証するより良い方法は、JLSに相談することです。コンパイル/逆コンパイルテストは、私が言ったことが1つのJavaコンパイラに有効であることのみを示しています。JLSで許可されていないことを除いて、他の人は(仮説的に)異なる動作をすることができます
スティーブンC

4
ただ参考までに:これは元々別の質問に投稿されていましたが、この質問の複製としてクローズされ、現在は統合されています。
Shog9 2012

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

Cでは未定義の動作あり、Javaの場合はこの回答を参照してください。何が起こるかはコンパイラに依存します。


4
いいえ、あなたが引用した答えによると、コンパイラに依存しません-編集してください-今のところ-1
Mr_and_Mrs_D

@Mr_and_Mrs_Dそれは何に依存しますか?
Mac

2
C_では未定義のbehavior_onlyです。コンパイラに依存していると言っても誤解を招きやすいです-コンパイラがこの動作を指定する必要があることを意味します。私は自分の投票を元に戻すが、あなたの答えを編集することを検討-編集を:おっと私はできません-あなたが持っている編集に最初:D
Mr_and_Mrs_D

16

のような構成x = x++;は、++演算子が何をしているのかおそらく誤解していることを示しています。

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

++演算子の削除に基づいて、同じことを行うようにこれを書き換えましょう:

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

さて、あなたがしたいこと(私が思うこと)に書き換えましょう:

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

ここで繊細は、その++オペレータの修正変数xのような表現とは異なり、x + xint型の値に評価されますが、変数のままになり、x自身が変わらず。由緒あるforループのような構成を考えてみましょう:

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

i++そこに気づきましたか?同じ演算子です。このforループを次のように書き換えると、同じように動作します。

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

また++、ほとんどの場合、より大きな式で演算子を使用しないことをお勧めします。ための繊細さの場合、それは前対後インクリメント(元の変数を変更++xし、x++それぞれ)、それは非常に簡単に追跡することが困難である微妙なバグを導入することです。


13

クラスファイルから取得したバイトコードによると

両方の割り当てはxをインクリメントしますが、違いはタイミングです when the value is pushed onto the stack

ではCase1、Pushはインクリメントの前に発生し(その後、割り当てられます)(基本的に、インクリメントが何もしないことを意味します)

ではCase2、インクリメントが最初に行われ(8になり)、スタックにプッシュされます(次にxに割り当てられます)。

ケース1:

int x=7;
x=x++;

バイトコード:

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

ケース2:

int x=7; 
x=++x;

バイトコード

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
  • ここでのスタックとは、オペランドスタックを指します。ローカル:xインデックス:1タイプ:int

答えを詳しく説明してください。
Nihar

Plsは、参照されているリンクとコメントを確認します
プライムプライム


8

増分後演算子は次のように機能します。

  1. オペランドの前の値を格納します。
  2. オペランドの値を増分します。
  3. オペランドの前の値を返します。

したがって、ステートメント

int x = 7;
x = x++; 

次のように評価されます。

  1. xは値7で初期化されます
  2. ポストインクリメント演算子は、xの以前の値、つまり7を格納して返します。
  3. xをインクリメントするため、xは8になります。
  4. xの以前の値、つまり7を返し、xに割り当てられるため、xは再び7になります。

したがって、xは確かに増加しますが、x ++は結果をxに割り当てているため、xの値は以前の値に上書きされます。


しかし、MSVCのxでのgccで8はいていると打ち鳴らすxは7です
夏日

7

xが呼び出された後にインクリメントが発生するため、xは依然として7です。++ xは、xが呼び出されたときに8に等しくなります。


7

値を再割り当てしxてもまだ7 x = ++xです。試してみると、8が得られます。

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

6

これは、x ++が変数に割り当てた後に値を増分するためです。以下同様に、この行の実行中に:

x++;

varialbe xは元の値(7)のままですが、次のような別の行でxを再び使用します。

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

8。

インクリメントされたxの値を割り当てステートメントで使用する場合は、

++x;

これにより、xが1増加し、その値が変数xに割り当てられます。

[編集] x = x ++ではなく、単にx ++です。前者はxの元の値をそれ自体に割り当てるため、実際にはその行では何もしません。


割り当て後にインクリメントすることを示すものと、8を印刷することを示すもの。割り当て前にインクリメントし、7と印刷する
R. Martinho Fernandes

xが元々7の場合、System.out.println(String.valueOf(x ++)); prints 7.同じプログラミング言語について話していることを確認しますか?
josephus

はい、そうです。このideone.com/kj2UUは、この回答の主張のように8を出力しません。
R.マルティーニョフェルナンデス2011

はい、私は間違っていました。x = x ++は、xをインクリメントする前に、最初にxに7を割り当てます。x ++(それ自体が割り当て)はx =(何でも)の前に最初に解決されるため、x =(何でも)でxに割り当てられた値が後に続きます。すみません、見ませんでした。
josephus

1
実際には、増分が最初に起こります。ideone.com/xOIDU
R.マルティーニョフェルナンデス

4

何が起こるか int x = 7; x = x++;ますか?

ans-> x++は、最初に式にxの値を使用し、次に値を1増やすことを意味します。
これがあなたの場合に起こります。RHSのxの値はLHSの変数xにコピーされ、次に値がx1増加します。

同様に、最初にxの値を1つ増やしてから、式で使用++x することを意味 ->します。
したがって、あなたのケースでは、あなた x = ++x ; // where x = 7
が8の値を得るでしょう。

より明確にするために、次のコードを実行するprintfステートメントの数を調べてください。

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

正しくない "RHSのxの値がLHSの変数xにコピーされ、xの値が1増加する"-これはx8になりますが、7です-読み取りと割り当ての間でインクリメントが発生します
user85421

3

++xプリインクリメントさ ->xがインクリメントされる前に使用されているが
x++、ポストインクリメントさ->xがインクリメントされた後に使用されています

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

1

つまり、これは次x++と等しくないことを意味します x = x+1

なぜなら:

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

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

そして今は少し奇妙に見えます:

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

非常にコンパイラに依存します!


2
そもそも同等だと言ったのは誰ですか?
Fortran、2011年

1
私があなただったら、すぐにこれらの本を捨てるxDとにかく、それは(x = x + 1, x-1)Cのようになり、コンマで区切られた式が許可されます。
Fortran、2011年

3
@fortran:さて、159ページの「Javaプログラミング言語、第3版」の私の10年前のコピーでは、「式i ++はi = i + 1と同等ですが、iは1回しか評価されません」と述べています。 ?最初の場所ジェームズ・ゴスリンで、それは、それが現れるのJava仕様のこの版のこの部分は非常に曖昧で不完全指定であり、私は後の版がより明確に、実際のオペレータのセマンティクスを表現するための言語をクリーンアップすることを推測。。
エリックリッペルト

2
@fortran:「iは一度だけ評価される」を除いて、標準は「M()。x ++」のような式がM()を一度だけ呼び出すことを伝えようとしています。あまり曖昧で、より正確な文言は違いがあることを強調してしまうその保管場所を決定する変数として私を評価して-ここでは、「一度だけ評価」が意味するものである- その保管場所への読み取りや書き込みが - -どちらも「評価済み」の合理的ではあるが誤った解釈である可能性があります。明らかに、保管場所は読み取りと書き込みの両方を行う必要があります。
Eric Lippert、2011年

1
「コンパイラに非常に依存」 -まったくありません!
スティーブンC

-1

x = x ++;

これはポストインクリメント演算子です。「オペランドの値を使用してから、オペランドをインクリメントする」と理解してください。

逆を実行する場合、つまり「オペランドをインクリメントしてからオペランドの値を使用する」場合は、以下に示すようにプリインクリメント演算子を使用する必要があります。

x = ++ x;

この演算子は、最初にxの値を1増やし、次にその値をxに割り当てます。


-1

この論争は、コードに踏み込むことなく、ただ考えるだけで解決できると思います。

関数としてi ++と++ iを考えてください。

今i = 7;
Func1(i ++)は7を返し、Func2(++ i)は8を返します(誰もが知っています)。内部的にはどちらの関数もiを8にインクリメントしますが、返す値は異なります。

したがって、i = i ++は関数Func1を呼び出します。関数内ではiが8にインクリメントされますが、完了すると関数は7を返します。

したがって、最終的に7がiに割り当てられます。(結局、i = 7)


2
ここには有効な「論争」はありません。コードは明らかに特定の方法で動作し、動作はJLSに準拠しています。それが違ったふるまいをするだろうと思っている人は誰もそれを試していないか、騙されています。(これは、7 x 7が49であると誰かが自分のタイムテーブルを忘れたときに「物議を醸す」と言っているようなものです...)
Stephen C

-2

これは、ポストインクリメント演算子を使用したためです。この次のコード行で

x = x++;

何が起こるかというと、xの値をxに割り当てています。x ++は、xの値がxに割り当てられた後、xを増分します。これが、インクリメント後の演算子の動作方法です。ステートメントが実行された後に機能します。したがって、コードではxが最初に返され、その後xがインクリメントされます。

あなたがしたなら

x = ++x;

プリインクリメント演算子を使用したため、答えは8になります。これは、xの値を返す前に、最初に値をインクリメントします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.