Precedence vs Associativity โ Two Different Rules
Precedence decides which operator gets evaluated first when two different operators sit next to each other โ * outranks +, so 2 + 3 * 4 is 2 + 12, not 5 * 4.
Associativity only matters when operators of the same rank appear together, and decides which direction they group โ left-to-right or right-to-left. 20 - 5 - 2 groups left-to-right as (20 - 5) - 2 = 13, while a = b = 5 groups right-to-left as a = (b = 5).
| Category (high โ low) | Operators | Associativity |
|---|---|---|
| Postfix | () [] -> . i++ i-- | Left โ Right |
| Unary | ++i --i + - ! ~ sizeof (type) | Right โ Left |
| Multiplicative | * / % | Left โ Right |
| Additive | + - | Left โ Right |
| Shift | << >> | Left โ Right |
| Relational | < <= > >= | Left โ Right |
| Equality | == != | Left โ Right |
| Bitwise AND | & | Left โ Right |
| Bitwise XOR | ^ | Left โ Right |
| Bitwise OR | | | Left โ Right |
| Logical AND | && | Left โ Right |
| Logical OR | || | Left โ Right |
| Ternary | ?: | Right โ Left |
| Assignment | = += -= *= /= | Right โ Left |
() liberally to make intent obvious, even when the default precedence would already give the right answer.Arithmetic: * and / Outrank + and -
#include <stdio.h> int main() { int a = printf("2 + 3 * 4 = %d\n", 2 + 3 * 4); // * first: 2 + 12 printf("(2 + 3) * 4 = %d\n", (2 + 3) * 4); // parens override printf("10 - 4 / 2 = %d\n", 10 - 4 / 2); // / first: 10 - 2 printf("20 %% 6 + 1 = %d\n", 20 % 6 + 1); // %% first: 2 + 1 return 0; }
2 + 3 * 4 = 14 (2 + 3) * 4 = 20 10 - 4 / 2 = 8 20 % 6 + 1 = 3
Relational Binds Tighter Than Logical
Relational operators (< > == !=) always run before logical operators (&& ||). This means age > 18 && marks > 50 is safe to write without extra parentheses โ but adding them anyway is good practice for readability.
#include <stdio.h> int main() { int age = 20, marks = 65; // evaluated as: (age > 18) && (marks > 50) if (age > 18 && marks > 50) printf("Eligible\n"); else printf("Not eligible\n"); // == also outranks &&, so this compares first, combines second int result = age == 20 && marks == 65; printf("result = %d\n", result); return 0; }
Eligible result = 1
The Bitwise-vs-Equality Trap
This one surprises even experienced programmers: bitwise & and | have lower precedence than ==. Writing if (flags & MASK == 0) does NOT check "is the masked bit zero" โ it secretly evaluates MASK == 0 first.
#include <stdio.h> int main() { int flags = 6; // binary 110 int MASK = 2; // binary 010 // WRONG (but compiles!): == runs before &, so this is flags & (MASK == 0) // = flags & 0 = 0 (always false, silently) printf("flags & MASK == 0 -> %d (misleading!)\n", flags & MASK == 0); // RIGHT: force the bitwise AND to happen first printf("(flags & MASK) == 0 -> %d (correct)\n", (flags & MASK) == 0); return 0; }
flags & MASK == 0 -> 0 (misleading!) (flags & MASK) == 0 -> 0 (correct)
= (Assignment) vs == (Equality)
= assigns a value and, as an expression, evaluates to that same value. Since C allows assignments inside if conditions, typing = instead of == by mistake doesn't cause an error โ it silently assigns and then tests whatever value was assigned.
#include <stdio.h> int main() { int flag = 0; // BUG: this ASSIGNS 5 to flag, then tests "is 5 truthy?" -> always true if (flag = 5) { printf("This always runs! flag is now %d\n", flag); } flag = 0; // reset // CORRECT: == compares without changing flag if (flag == 5) { printf("Won't print.\n"); } else { printf("Correctly false. flag is still %d\n", flag); } return 0; }
This always runs! flag is now 5 Correctly false. flag is still 0
if (5 == flag) โ if you accidentally type one =, the compiler flags "5 = flag" as an error immediately, since 5 isn't a variable you can assign to.Right-to-Left Associativity: Unary & Ternary
Unary operators (++i, -x, sizeof) and the ternary ?: associate right-to-left โ the opposite of most operators. This is what lets a = b = c = 5; chain naturally: it's really a = (b = (c = 5)).
#include <stdio.h> int main() { int a, b, c; a = b = c = 5; // groups right-to-left: a = (b = (c = 5)) printf("a=%d b=%d c=%d\n", a, b, c); int marks = 72; // ternary is right-to-left too, but here it's just one condition char *result = (marks >= 50) ? "Pass" : "Fail"; printf("Result: %s\n", result); int x = 5; printf("-x * 2 = %d\n", -x * 2); // unary - binds to x first, then *2 return 0; }
a=5 b=5 c=5 Result: Pass -x * 2 = -10
Quick Quiz
What does 2 + 3 * 4 evaluate to?
Why does flags & MASK == 0 often behave unexpectedly?
What's wrong with if (flag = 5) when the intent was a comparison?
Which operators associate right-to-left?
Lesson Checklist
- I understand the difference between precedence and associativity
- I know * and / outrank + and -
- I know relational operators outrank && and ||
- I know == outranks bitwise & and | โ and always parenthesize when mixing them
- I can explain the = vs == bug in an if condition
- I know assignment and unary operators associate right-to-left
- I completed the quiz