Data structures & algorithms · Foundations
Operators, expressions & precedence
Code is not read left to right. This page collapses one mixed expression down to a single value, one operator at a time, so you can see exactly where your reading and the compiler's part company.
Collapse an expression yourself →01 The idea
Every expression is a collapse, not a sentence
You will write more expressions than anything else. An expression is any piece of code that produces a value: 4729, n % 10, i < n && a[i] != 0. The compiler does not scan one left to right the way your eye does. It first decides which operator owns which operands, using a fixed ladder of rungs, and only then computes anything.
Get that grouping wrong and nothing crashes. The code compiles, runs, and quietly returns a number that is close enough to look right. a + b / 2 is a bug you can only find by reading, which is exactly why interviewers keep asking about it.
02 Hand trace
Taking 4729 apart with two operators
Before precedence, meet the workhorse. Take 4729. Two operators are enough to strip it digit by digit: % reads the last digit, and / throws that digit away. Every row below is one turn of the loop. The rose box is the digit % 10 is about to hand you; the green row is where the loop stops.
Read the two halves of each note separately, because they do different jobs. % 10 never changes n; it hands you a copy of the last digit and leaves the number alone. / 10 is the one that shrinks n, and it only works because whole-number division throws the remainder away — 4729 / 10 is 472, not 472.9. That is what / does on two whole numbers in C, C++ and Java; Python spells the same operation //, because a plain / there would hand you 472.9 and the trick would fall apart. Run the pair in a loop and the digits arrive backwards: 9, 2, 7, 4. That is why the same four digits that add up to 22 also spell 9274, and it is why % 10 and / 10 show up in digit sums, palindrome checks and reversals alike.
03 Code
The same two digits, one line apart
Now write it down and watch precedence break it. Both blocks below try to average the last two digits of 4729, which are 9 and 2. Every variable here is a whole number — C, C++ and Java spell that int — so / truncates, exactly as it did in the trace. The left block reads more naturally out loud. It is also wrong, and it never says so.
Reads well · computes the wrong thing
// average of 4729's last two digitsint n = 4729;int avg = n % 10 + n / 10 % 10 / 2;// collapses to 9 + (2 / 2) -> 9 + 1// avg = 10, and 10 is not between 9 and 2
Named parts · says what it means
int n = 4729;int last = n % 10; // 9int next = n / 10 % 10; // 2int avg = (last + next) / 2;// (9 + 2) / 2 -> 11 / 2 -> avg = 5
/ sits a rung above +. In the left version the / 2 grabs only the operand touching it, which is the tens digit, not the sum. The line means 9 + (2 / 2), so it halves one digit and adds the other whole. Brackets are the only way to pull a + up above a /.
n / 10 % 10 needs no brackets, and that is not luck. / and % share a rung, and that rung runs left to right, so it divides first and takes the remainder second — exactly the two moves from the trace. Bracket it the other way and you get n / (10 % 10), which is division by zero.
Two names beat one clever line. Once last and next exist, the average reads like an average and the precedence question stops being a question. The two blocks do not agree — the left one answers 10 and the right one answers 5 — but the named version and the one-line (n % 10 + n / 10 % 10) / 2 do, and any optimising compiler turns them into the same machine work. The clarity is free.
05 Cheat sheet
The ladder, tightest rung at the top
| Rung | Operators | What catches people out |
|---|---|---|
| 1 · brackets | ( ) | The only override there is. Free to add and never wrong. |
| 2 · unary | ++ -- ! -x | Binds to one operand, tighter than any arithmetic. !a == b is (!a) == b. |
| 3 · multiplicative | * / % | Left to right, so 100 / 7 * 7 is 98. Whole-number / truncates before * ever runs. |
| 4 · additive | + - | A rung below /, so a + b / 2 halves b alone and is not the average. |
| 5 · relational | < <= > >= | Below all arithmetic, so i < n - 1 needs no brackets. |
| 6 · equality | == != | Below the size comparisons, so a < b == c compares a true/false against c. |
| 7 · logical AND | && | Left side first. If it is false the right side never runs at all. |
| 8 · logical OR | || | A rung below &&, so a || b && c is a || (b && c). |
| 9 · ternary | ? : | Below every logical operator, so the whole condition is settled before ? is reached. |
| 10 · assignment | = += -= *= /= %= | Loosest of all, and it runs right to left: a = b = 0 means a = (b = 0). The full right-hand side is computed first. |
06 Where & why
When to trust the ladder and when to type brackets
Nobody gives you marks for writing an expression with no brackets in it. Precedence exists so that ordinary arithmetic reads like ordinary arithmetic. Past that point brackets cost you two keystrokes and save the next reader a trip to a reference table, so the real skill is knowing the handful of places where the ladder is genuinely obvious.
Lean on precedence when…
Use brackets or names instead when…
| Situation | Better choice | Why |
|---|---|---|
| You mixed && and || in one condition | Write (a && b) || c | && is a rung above ||, so the unbracketed line is not what most people read. The compiler warns for the same reason. |
| You are averaging or scaling a sum | Write (a + b) / 2 | / is a rung above +, so a + b / 2 halves b alone — the exact bug in section 03. |
| One line mixes arithmetic, a comparison and a logical operator | Two named variables | Three rungs in one line is where review comments come from. A name explains the intent; a bracket only fixes the grouping. |
| You are counting on which operand runs first | Separate statements | Precedence decides grouping, not order of evaluation. In C, f() + g() may call g first, and i = i++ + 1 is undefined behaviour. |
07 Interview questions
Say these out loud
Question three gets asked on a whiteboard with no compiler in the room. Practise talking through the collapse rather than announcing the answer.
What is operator precedence?
What is associativity, and when does it actually matter?
Evaluate 10 + 4729 % 10 * 2 - 1 and talk me through it.
Why does n % 10 give the last digit?
What does 100 / 7 * 7 give in C or Java, and why is it not 100?
What is short-circuit evaluation?
Give me a case where short-circuiting is a correctness requirement, not an optimisation.
What is the difference between i++ and ++i?
What does x *= a + b expand to?
Is a < b < c valid C, and what does it do?
& versus && — what is the difference?
When does precedence actually bite you in real code, and what do you do about it?
08 Practice problems
Bracket it before you run it
Work each one out on paper first and write down the value you expect. The ones that are a single expression you can then type straight into the console above; the rest you check by reading the ladder. The gap between what you expected and what comes back is the part worth studying.