问题
I have two similar questions about operator precedences in Java.
First one:
int X = 10;
System.out.println(X++ * ++X * X++); //it prints 1440
According to Oracle tutorial:
postfix (expr++, expr--) operators have higher precedence than prefix (++expr, --expr)
So, I suppose that evaluation order:
1) first postfix operator: X++
1.a) X++ "replaced" by 10
1.b) X incremented by one: 10+1=11
At this step it should look like: System.out.println(10 * ++X * X++), X = 11;
2) second POSTfix operator: X++
2.a) X++ "replaced" by 11
2.b) X incremented by one: 11+1=12
At this step it should look like: System.out.println(10 * ++X * 11), X = 12;
3) prefix operator: ++X
3.a) X incremented by one: 12+1=13
3.b) ++X "replaced" by 13
At this step it should look like: System.out.println(10 * 13 * 11), X = 13;
4) evaluating 10*13 = 130, 130*11 = 1430.
But Java seems to ignore PRE/POST ordering and puts them on one level. So the real order:
X++ -> ++X -> X++
what causes the answer to be (10 * 12 * 12) = 1440.
Second one:
Example from this question:
int a=1, b=2;
a = b + a++;
Part of accepted answer:
"By the time of assignment, ++
has already incremented the value of a
to 2
(because of precedence), so =
overwrites that incremented value."
OK, let's look step-by-step:
1) replacing "b" with 2
2) replacing "a++" with 1
3) incrementing "a" by 1 -> at this point a==2
4) evaluating 2+1 = 3
5) overwriting incremented value of "a" with 3
Seems everything is fine. But let's make a little change in that code (replace "=" with "+=")
a += b + a++;
steps 1-4 should be same as above. so, after step 4 we have something like that:
a += 3;
where a==2
And then I think: OK, a = 2+3
, so a
should be 5
. BUT the answer is only 4
I'm really confused. I already spent couple of hours but still can't understand where I am wrong.
P.S. I know, that I shouldn't use this "style" in real applications. I just want to understand what is wrong in my thoughts.
回答1:
The confusion stems from the fact that the operands are evaluated from left to right. This is done first, before any attention is paid to operator precedence/order of operations.
This behavior is specified in JLS 15.7.2. Evaluate Operands before Operation
So X++ * ++X * X++
is first evaluated as 10 * 12 * 12
which yields, as you saw, 1440.
To convince yourself of this, consider the following:
X = 10; System.out.println(X++ * ++X);
X = 10; System.out.println(++X * X++);
If X++
were done first, then ++X
second, then multiplication, both should print the same number.
But they do not:
X = 10; System.out.println(X++ * ++X); // 120
X = 10; System.out.println(++X * X++); // 121
So how does this make sense? Well if we realize that operands are evaluated from left to right, then it makes perfect sense.
X = 10; System.out.println(X++ * ++X); // 120 (10 * 12)
X = 10; System.out.println(++X * X++); // 121 (11 * 11)
The first line looks like
X++ * ++X
10 (X=11) * (X=12) 12
10 * 12 = 120
and the second
++X * X++
(X=11) 11 * 11 (X=12)
11 * 11 = 121
So why are prefix and postfix increment/decrement operators in the table?
It is true that increment and decrement must be performed before multiplication. But what that is saying is that:
Y = A * B++
// Should be interpreted as
Y = A * (B++)
// and not
Y = (A * B)++
Just as
Y = A + B * C
// Should be interpreted as
Y = A + (B * C)
// and not
Y = (A + B) * C
It remains that the order of the evaluation of the operands occurs left-to-right.
If you're still not conviced:
Consider the following program:
class Test
{
public static int a(){ System.out.println("a"); return 2; }
public static int b(){ System.out.println("b"); return 3; }
public static int c(){ System.out.println("c"); return 4; }
public static void main(String[] args)
{
System.out.println(a() + b() * c());
// Lets make it even more explicit
System.out.println(a() + (b() * c()));
}
}
If the arguments were evaluated at the time they were needed, either b
or c
would come first, the other next, and lastly a
. However, the program outputs:
a b c 14 a b c 14
Because, regardless of the order that they're needed and used in the equation, they're still evaluated left to right.
Helpful reading:
- What are the rules for evaluation order in Java?
- a += a++ * a++ * a++ in Java. How does it get evaluated?
- Appendix A: Operator Precedence in Java
回答2:
The reason why its 1440 is because
x is set to 10 i.e 1st term is FIXED(overall equation 10 *)
x is incremented by 1,x =11 now
x is pre-incremented by 1 x=12 and second term FIXED now (overall equation 10 * 12 *)
now x is set to 12 and third term FIXED(overall equation 10 * 12 *12)
x is increment now but is in this case not used for evaluation,
in short a term is FIXED when variable occurs which in this case is X
2nd case: I'm not sure but I guess can be broken as,
a=b+a
a++
which I think is what is happening.
回答3:
In short,
Precedence is like preparing the expression to be calculated by putting parentheses. Evaluation comes next from left to right considering each pair of parentheses as a separate operation.
For example,if i=2
then i+i++
becomes i+(i++)
after precedence and evaluates to 2+2 = 4
.
However, i+++i
becomes (i++)+i
and evaluates to 2+3 = 5
.
Same to i+(i=5)
which evaluates to 2+5 = 7
.
In fact the postfix operators do have higher precedence than prefix operators. For example, i+++++i
becomes ((i++)++)+i
after precedence which gives a compile error (the second postfix operator needs a variable to operate on but a value is found instead!). If both postfix and prefix operators had had equal precedence then the expression would have become (i++)+(++i)
and evaluates to 2+4 = 6
.
If you need more explanation you can compile and run the following code and inspect the examples printed at the output.
public class TestPrecedence {
public static void main(String[] str) {
int i = 0;
System.out.println("\n");
i = 2; System.out.println("i = " + i + "\n");
i = 2; System.out.println("i++ = " + i++ + "\n");
i = 2; System.out.println("++i = " + ++i + "\n");
i = 2; System.out.println("i++i = (i++)i TestPrecedence.java:8: error: ')' expected\n"+
" i++i\n"+
" ^\n");
i = 2; System.out.println("i+-i = i+(-i) = " + (i+-i) + "\n");
i = 2; System.out.println("++i++ = ++(i++) TestPrecedence.java:12: error: unexpected type\n"+
" ++i++ \n"+
" ^\n"+
" required: variable\n"+
" found: value\n");
i = 2; System.out.println("i+++++i = ((i++)++)+i TestPrecedence.java:17: error: unexpected type\n"+
" i+++++i\n"+
" ^\n"+
" required: variable\n"+
" found: value\n");
i = 2; System.out.println("i++ + ++i = " + (i++ + ++i) + "\n");
i = 2; System.out.println("i+(i=3) = " + (i+(i=3)) + " evaluates left to right\n");
i = 2; System.out.println("i+i++ precedence yields i+(i++) evaluates to 2+2 = " + (i+i++) + "\n");
i = 2; System.out.println("i+++i precedence yields (i++)+i evaluates to 2+3 = " + (i+++i) + "\n");
System.out.println("\n");
}
}
回答4:
For the second one ->
int a=1, b=2;
a += b + a++;
Compiler will convert it to
a = a + b + a++;
Then apply your logic and you will find the reason why a is 4.
回答5:
First step
1) first postfix operator: X++
1.a) X++ "replaced" by 10
1.b) X incremented by one: 10+1=11
At this step it should look like: System.out.println(10 * ++X * X++), X = 11;
2) prefix operator: ++X
2.a) X "replaced" by 11
2.b) X incremented by one: 11+1=12
At this step it should look like: System.out.println(10 * 12 * X++), X = 12;
3) second POSTfix operator: X++
3.a) X "replaced" by 12
3.b) X incremented by one: 12+1=13
At this step it should look like: System.out.println(10 * 12 * 12), X = 13;
This prints 10 * 12 * 12 = 1440
This is the bytecode for the first step
1. bipush 10
2. istore_0
3. getstatic java/lang/System/out Ljava/io/PrintStream;
4. iload_0
5. iinc 0 1
6. iinc 0 1
7. iload_0
8. imul
9. iload_0
10. iinc 0 1
11. imul
12. invokevirtual java/io/PrintStream/println(I)V
13. return
This do the following:
1. Push 10 to the stack
2. Pop 10 from the stack to X variable
3. Push X variable value (10) to the stack
5. Increment local variable X (11)
6. Increment local variable X (12)
7. Push X variable value (12) to the stack
8. Multiply top and subtop (10*12) Now Top = 120
9. Push X variable value (12) to the stack
10. Increment local variable X (13)
11. Multiply top and subtop (120*12) Now Top = 1440
Notice than the last increment (10.) is done after the push of X to the stack
来源:https://stackoverflow.com/questions/18991306/java-pre-postfix-operator-precedences