If Parenthesis Has a Higher Precedence Then Why Is Increment Operator Solved First

If parenthesis has a higher precedence then why is increment operator solved first?

Expressions are evaluated left to right. Parentheses (and precedence) just express grouping, they don't express ordering of evaluation.

So

 11 * (12 + 5)
++a ++a

equals

187

Why highest priority operation [ ] is not evaluated first ini = i + arr[i++] + arr[i++]?

See jls Evaluate Operands before Operation:

The Java programming language guarantees that every operand of an
operator (except the conditional operators &&, ||, and ? :) appears to
be fully evaluated before any part of the operation itself is
performed.

and Evaluate Left-Hand Operand First

The left-hand operand of a binary operator appears to be fully
evaluated before any part of the right-hand operand is evaluated.

The most left operand is evaluated as 1 before i++.

Why is the operator precedence not followed here?

The expression (++y * (y++ + 5)); will be placed in a stack something like this:

1. [++y]
2. [operation: *]
3. [y++ + 5] // grouped because of the parenthesis

And it will be executed in that order, as result

1. 10+1 = [11] // y incremented 
2. [operation: *]
3. 11+5 = [16] // y will only increment after this operation

The the expression is evaluated as

11 * 16 = 176

Clarification on math expression using prefix increment operator and bracket precedence

this is the class file your code compiled:

   int k = 8;
byte var10000 = k;
int k = k + 1;
int p = var10000 * (k - 8);
System.out.println(p);

Operator Precedence.. () and ++

As usual, this is undefined behaviour. There is no sequence point at the +, so it is not defined at what point the ++ updates a. This is not a precedence issue.

Differences between Java order of expression, operator precedence and associativity

You are correct, they misspoke, assignment operator has the lowest order of precedence.

You are incorrect, they never mention "order of evaluation" anywhere (that you've shown, anyway). The code shown doesn't do anything where order of evaluation matters. The assignment has nothing to with order of evaluation.





  1. The code does not compile because the assignment operator has the highest order of precedence in this expression.

Operator precedence shows:

9   >   relational
2 ?: ternary
1 = assignment

Which means that to explicitly show precedence using parenthesis, the statement becomes:

statement = ((250 > 338) ? lion : tiger) = " is Bigger";



  1. Both sides of the ternary operator must have the same type. This expression is invalid, as the left side of the second assignment operator is not a variable, so the answer is option F.

The ternary operator being ((250 > 338) ? lion : tiger), "both sides" refer to the two assignment operators.

As it says, "This expression is invalid, as the left side of the second assignment operator is not a variable".




  1. Note that if the question had added explicit parentheses around the expression (Tiger = " is Bigger"), option E would have the correct output.

You already confirmed that yourself.

To explicitly show precedence using parenthesis, the statement becomes:

statement = ( (250 > 338) ? lion : (tiger = " is Bigger") );

Java primitive variable reassignment during expression

a += (a = 4);

The above is logically equivalent to the following:

a = a + (a = 4);

If we substitute in the existing value for a, then this simplifies to:

a = 10 + 4 = 14

We can do the same for b:

b = b + (b = 5) = 20 + 5 = 25

We see this result because of operator precedence. The addition operator, +, has a higher precedence than the assignment operator, =, as defined by the Java operator precedence table.

You can see that the addition-assignment operator, +=, shares the same precedence with the assignment operator, in which case the expression is evaluated from left to right.


If, instead, the expressions were:

a = (a = 4) + a;
b = (b = 5) + b;

Then it would result in the output that you expect (a = 8, b = 10), as the left operand is computed before the right operand (when evaluating the expression). I'll try to locate where this is specified in the Java Language Specification.

How is precedence working in C for this expression

In an operator precedence table, the lower the number, the more tightly it binds its operands.

The expression i < j || ++j < k can be written as (i < j) || ((++j) < k).

Precedence doesn't tell you the order in which things happen, it tells you how to group things. From (i < j) || ((++j) < k), the operator || evaluates the left side first. If it's true, the right side never gets executed. The fact that ++ has a high precedence doesn't change the behaviour of ||.

Operator Precedence vs Order of Evaluation

Yes, the MSDN article is in error, at least with respect to standard C and C++1.

Having said that, let me start with a note about terminology: in the C++ standard, they (mostly--there are a few slip-ups) use "evaluation" to refer to evaluating an operand, and "value computation" to refer to carrying out an operation. So, when (for example) you do a + b, each of a and b is evaluated, then the value computation is carried out to determine the result.

It's clear that the order of value computations is (mostly) controlled by precedence and associativity--controlling value computations is basically the definition of what precedence and associativity are. The remainder of this answer uses "evaluation" to refer to evaluation of operands, not to value computations.

Now, as to evaluation order being determined by precedence, no it's not! It's as simple as that. Just for example, let's consider your example of x<y<z. According to the associativity rules, this parses as (x<y)<z. Now, consider evaluating this expression on a stack machine. It's perfectly allowable for it to do something like this:

 push(z);    // Evaluates its argument and pushes value on stack
push(y);
push(x);
test_less(); // compares TOS to TOS(1), pushes result on stack
test_less();

This evaluates z before x or y, but still evaluates (x<y), then compares the result of that comparison to z, just as it's supposed to.

Summary: Order of evaluation is independent of associativity.

Precedence is the same way. We can change the expression to x*y+z, and still evaluate z before x or y:

push(z);
push(y);
push(x);
mul();
add();

Summary: Order of evaluation is independent of precedence.

When/if we add in side effects, this remains the same. I think it's educational to think of side effects as being carried out by a separate thread of execution, with a join at the next sequence point (e.g., the end of the expression). So something like a=b++ + ++c; could be executed something like this:

push(a);
push(b);
push(c+1);
side_effects_thread.queue(inc, b);
side_effects_thread.queue(inc, c);
add();
assign();
join(side_effects_thread);

This also shows why an apparent dependency doesn't necessarily affect order of evaluation either. Even though a is the target of the assignment, this still evaluates a before evaluating either b or c. Also note that although I've written it as "thread" above, this could also just as well be a pool of threads, all executing in parallel, so you don't get any guarantee about the order of one increment versus another either.

Unless the hardware had direct (and cheap) support for thread-safe queuing, this probably wouldn't be used in in a real implementation (and even then it's not very likely). Putting something into a thread-safe queue will normally have quite a bit more overhead than doing a single increment, so it's hard to imagine anybody ever doing this in reality. Conceptually, however, the idea is fits the requirements of the standard: when you use a pre/post increment/decrement operation, you're specifying an operation that will happen sometime after that part of the expression is evaluated, and will be complete at the next sequence point.

Edit: though it's not exactly threading, some architectures do allow such parallel execution. For a couple of examples, the Intel Itanium and VLIW processors such as some DSPs, allow a compiler to designate a number of instructions to be executed in parallel. Most VLIW machines have a specific instruction "packet" size that limits the number of instructions executed in parallel. The Itanium also uses packets of instructions, but designates a bit in an instruction packet to say that the instructions in the current packet can be executed in parallel with those in the next packet. Using mechanisms like this, you get instructions executing in parallel, just like if you used multiple threads on architectures with which most of us are more familiar.

Summary: Order of evaluation is independent of apparent dependencies

Any attempt at using the value before the next sequence point gives undefined behavior -- in particular, the "other thread" is (potentially) modifying that data during that time, and you have no way of synchronizing access with the other thread. Any attempt at using it leads to undefined behavior.

Just for a (admittedly, now rather far-fetched) example, think of your code running on a 64-bit virtual machine, but the real hardware is an 8-bit processor. When you increment a 64-bit variable, it executes a sequence something like:

load variable[0]
increment
store variable[0]
for (int i=1; i<8; i++) {
load variable[i]
add_with_carry 0
store variable[i]
}

If you read the value somewhere in the middle of that sequence, you could get something with only some of the bytes modified, so what you get is neither the old value nor the new one.

This exact example may be pretty far-fetched, but a less extreme version (e.g., a 64-bit variable on a 32-bit machine) is actually fairly common.

Conclusion

Order of evaluation does not depend on precedence, associativity, or (necessarily) on apparent dependencies. Attempting to use a variable to which a pre/post increment/decrement has been applied in any other part of an expression really does give completely undefined behavior. While an actual crash is unlikely, you're definitely not guaranteed to get either the old value or the new one -- you could get something else entirely.


1 I haven't checked this particular article, but quite a few MSDN articles talk about Microsoft's Managed C++ and/or C++/CLI (or are specific to their implementation of C++) but do little or nothing to point out that they don't apply to standard C or C++. This can give the false appearance that they're claiming the rules they have decided to apply to their own languages actually apply to the standard languages. In these cases, the articles aren't technically false -- they just don't have anything to do with standard C or C++. If you attempt to apply those statements to standard C or C++, the result is false.



Related Topics



Leave a reply



Submit