Почему этот приоритет Java-оператора здесь игнорируется?
Следующий код выводит "3", а не "4", как вы могли ожидать.
public class Foo2 {
public static void main(String[] args) {
int a=1, b=2;
a = b + a++;
System.out.println(a);
}
}
Я понимаю как. Постфиксный инкремент происходит после того, как значение "а" было загружено. (Увидеть ниже).
Что я не совсем понимаю, так это почему. Приоритет оператора postfix ++ выше, чем +, поэтому он не должен выполняться первым?
% 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
7 ответов
Постфикс ++
увеличивает значение переменной и возвращает значение, которое было там до приращения. Таким образом, возвращаемое значение operator++
в вашем примере будет 1
и конечно 1 + 2
дам 3
, который затем присваивается a
, К моменту назначения, ++
уже увеличил значение a
в 2
(из-за приоритета), так =
перезаписывает это увеличенное значение.
Приоритет оператора здесь не игнорируется.
Единственная немного запутанная вещь о a++
это постфикс ++
Оператор имеет два разных эффекта:
- это увеличивает переменную, к которой применяется один
- у него есть возвращаемое значение, равное значению переменной, прежде чем она будет увеличена
Так что если a
имеет 1 и b
имеет значение 2 перед этой строкой:
a = b + a++;
Затем выполняются следующие шаги:
- оценивать
b
- выражение
b
имеет значение 2, поэтому запомните значение 2
- выражение
- оценивать
a++
- выражение
a++
имеет значение 1, поэтому запомните значение 1 - увеличить значение в переменной
a
на единицу, так что теперь он имеет значение 2
- выражение
- добавить результаты двух выражений (2 и 1 соответственно)
- 2 + 1 = 3
- присвоить 3 переменной
a
Как видите, код эффективно присваивает два значения a
:
- 2 назначен
a
во время оценкиa++
- 3 назначен
a
в результате выполнения задания
Поскольку второе назначение происходит после первого, вы видите только эффект второго и всегда будете наблюдать a
как имеющий значение 3 после этой строки.
Изменить: я постараюсь дать интерпретацию декомпилированного кода. Это может быть немного сложным, если вы не знаете, как JVM работает внутри (т.е. вы знаете, как JVM является виртуальной машиной на основе стека и что это значит):
// 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-4 просто реализуют
int a=1, b=2;
Линии 4-10 реализуют
a = b + a++;
Я пропустил другие строки, поскольку там больше ничего интересного не происходит.
Как интересная заметка: ясно видно, что этот код вообще не оптимизирован. Причина этого заключается в том, что оптимизация является задачей среды выполнения (т.е. JVM) в мире Java, а не компилятора (javac
например).
Оператор постинкремента / декремента (a++) возвращает значение до приращения. Прединкремент / декремент (++a) возвращает значение после приращения.
У меня была та же проблема с этим определением приоритета оператора (как определено здесь), и я думаю, что ни один из вышеупомянутых ответов точно не объясняет и не разъясняет парадокс в этом определении. Это то, что я считаю более высоким приоритетом постфиксного оператора к другим операторам (в данном примере к бинарному оператору плюс).
Рассмотрим следующие фрагменты кода:
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;
который имеет две возможные оценки, будет оцениваться как z = x++ + y;
по компилятору Java. Это означает, что из трех знаков плюс, которые пришли вместе, компилятор принимает первые два из них как постфиксный оператор, а третий - как бинарный оператор плюс. Фактически это результат более высокого приоритета постфиксного оператора над другими операторами.
Второй фрагмент кода показывает, как выходные данные отличаются, записывая выражение как z = x + ++y;
который явно указывает, какой знак плюс является бинарным оператором.
Это не вопрос приоритета, это вопрос определения оператора. По определению постфиксный оператор выполняется после того, как переменная используется во включающем выражении.
Оператор postfix ++ говорит:
Используйте исходное значение переменной в любом уравнении, а затем постепенно увеличивайте значение переменной.
Я никогда не видел
a = b + a++;
будучи использованным, это кажется мне плохим кодированием. Используя это, я думаю, это также означает, что вы могли бы написать:
int a++ = 1;
который не работает.
обычно вы увидите
int a = 1;
int b = 2;
a = b + a; // 3
a = 1;
a++;
a = b + a; // 4