5

次のコードは、ご想像のとおり「4」ではなく、「3」を出力します。

public class Foo2 {
    public static void main(String[] args) {
        int a=1, b=2;             
        a = b + a++;
        System.out.println(a);
    } 
}

私はその方法を理解しています。"a" の値がロードされた後、接尾辞のインクリメントが発生します。(下記参照)。

私がよく理解していないのは、その理由です。後置 ++ の演算子の優先順位は + よりも高いので、最初に実行するべきではありませんか?

% javap -c Foo2

Compiled from "Foo2.java"
public class Foo2 extends java.lang.Object{
public Foo2();
  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_1
   1:   istore_1
   2:   iconst_2
   3:   istore_2
   4:   iload_2
   5:   iload_1
   6:   iinc    1, 1
   9:   iadd
   10:  istore_1
   11:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   14:  iload_1
   15:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   18:  return
4

7 に答える 7

20

Postfix++は variable の値をインクリメントし、インクリメント前の値を返しますoperator++したがって、あなたの例では の戻り値はに1なり、もちろん1 + 2が与えられ3、それが に割り当てられaます。代入時までに、++すでに to の値がインクリメントaされている2ため (優先順位のため)、=そのインクリメントされた値を上書きします。

于 2009-09-28T21:35:21.963 に答える
13

ここでは、演算子の優先順位は無視されていません。

少し紛らわしいa++のは、後置++演算子には 2 つの異なる効果があることです。

  1. 適用される変数を1つ増やします
  2. 増加するの変数の値と等しい戻り値があります

したがって、この行の前にa1bがあり、値が 2 の場合:

a = b + a++;

次に、次の手順が行われます。

  • 評価b
    • bの値は 2 なので、値 2 を覚えておいてください
  • 評価a++
    • a++の値は 1 なので、値 1 を覚えておいてください
    • variable の値をa1 増やして、値 2 を保持します。
  • 2 つの式の結果 (それぞれ 2 と 1) を加算します。
  • 2 + 1 = 3
  • 変数に 3 を割り当てますa

ご覧のとおり、コードは に 2 つの値を効果的に割り当てますa

  • aの評価中に2 が割り当てられます。a++
  • a割り当ての結果として3 が割り当てられます

a2 番目の代入は最初の代入の後に発生するため、2 番目の代入の効果のみが表示され、その行の後に値 3 があることが常に観察されます。

編集:逆コンパイルされたコードの解釈を提供しようとします。JVM が内部でどのように動作するかを理解していない限り (つまり、JVM がスタックベースの VM であることと、それが何を意味するかを理解していなければ)、理解するのは少し難しいかもしれません。

   // Push the constant 1 on the stack
   0:   iconst_1
   // Pop the topmost value from the stack (1) and store it in the local variable #1 (a.k.a "a")
   1:   istore_1
   // Push the constant 2 on the stack
   2:   iconst_2
   // Pop the topmost value from the stack (2) and store it in the local variable #2 (a.k.a "b")
   3:   istore_2
   // Load the local variable #2 ("b") and push its value (2) on the stack
   4:   iload_2
   // Load the local variable #1 ("a") and push its value (1) on the stack
   5:   iload_1
   // Increment the local variable #1 by 1 (this action does not use the stack!)
   6:   iinc    1, 1
   // Pop the 2 topmost values from the stack (2 and 1), add them and push the result (3) back on the stack
   9:   iadd
   // Pop the topmost value from the stack (3) and store it in local variable #1 ("a")
   10:  istore_1

行 0-3 は単純に実装します

int a=1, b=2;

行 4-10 実装

a = b + a++;

興味深いことはもう何も起こらないので、他の行は省略しました。

興味深い補足として、このコードがまったく最適化されていないことは明らかです。これは、Java の世界では、最適化はランタイム環境 (つまり JVM) のタスクであり、コンパイラ (javacたとえば) のタスクではないためです。

于 2009-09-28T21:40:25.280 に答える
3

ポストインクリメント/デクリメント演算子 (a++) は、インクリメント前の値を返します。プレインクリメント/デクリメント (++a) は、インクリメント後の値を返します。

于 2009-09-28T21:58:22.900 に答える
2

この演算子の優先順位の定義 (ここで定義) でも同じ問題がありましたが、上記の回答のどれも、この定義のパラドックスを正確に説明および明確化していないと思います。これは、後置演算子が他の演算子 (この例ではバイナリプラス演算子) よりも優先されることの意味だと思います。

次のコード フラグメントを検討してください。

    int x = 1, y =4 , z;
    z = x+++y;  // evaluates as: x++ + y
    System.out.println("z : " + z); // z: 5
    System.out.println("y : " + y); // y: 4
    System.out.println("x : " + x); // x: 2

    x = 1; y =4 ; 
    z = x + ++y;
    System.out.println("z : " + z); // z: 6
    System.out.println("y : " + y); // y: 5
    System.out.println("x : " + x); // x: 1

ご覧のとおり、z = x+++y;2 つの評価が可能な単一の式はz = x++ + y;、Java コンパイラによって評価されます。これは、一緒になった 3 つのプラス記号から、コンパイラーがそれらのうちの最初の 2 つを後置演算子として、3 つ目を 2 項プラス演算子として想定することを意味します。これは実際には、後置演算子が他の演算子よりも優先される結果です。

2 番目のコード フラグメントは、どのz = x + ++y;プラス記号が二項演算子であるかを明示的に指定する式を記述することによって、出力がどのように異なるかを示しています。

于 2014-02-22T20:45:23.980 に答える
1

これは優先順位の問題ではなく、演算子の定義の問題です。 定義により、後置演算子は、変数が外側の式で使用された後に実行されます。

于 2009-09-28T21:35:58.470 に答える
0

後置 ++ 演算子が言っていることは次のとおりです。

どの方程式でも変数の元の値を使用し、後で変数をインクリメントします。

于 2009-09-28T21:37:04.850 に答える