Precedence decides where the invisible parentheses go; associativity breaks ties between operators on the same level. Neither decides the order in which operands are evaluated — see the warning at the bottom.
| lvl | operators | what they are | assoc. | watch out | learn |
|---|---|---|---|---|---|
| 1 | () [] . -> x++ x-- (T){…} | call, index, member, arrow, postfix inc/dec, compound literal | left → right | x++ yields the old value, then bumps x | → lesson |
| 2 | ++x --x + - ! ~ * & (T) sizeof _Alignof | prefix inc/dec, unary plus/minus, logical & bitwise NOT, dereference, address-of, cast, sizeof (C23: alignof) | right → left | level 1 beats level 2: *p++ is *(p++), *p.x is *(p.x) | → lesson |
| 3 | * / % | multiply, divide, remainder | left → right | all three share the level: 2 + 3 * 4 % 5 = 2 + ((3*4) % 5) = 4 | → lesson |
| 4 | + - | add, subtract (binary) | left → right | a - b - c is (a-b)-c, not a-(b-c) | → lesson |
| 5 | << >> | bit shifts | left → right | below +: a << b + c is a << (b+c) | → lesson |
| 6 | < <= > >= | relational | left → right | a < b < c is (a<b) < c — compares a 0/1 with c, not a range test | → lesson |
| 7 | == != | equality | left → right | a == b == c is (a==b) == c — same 0/1 trap | → lesson |
| 8 | & | bitwise AND | left → right | below ==: a & b == c is a & (b==c) — the most famous trap in C | → lesson |
| 9 | ^ | bitwise XOR | left → right | same story as & — always parenthesize masks before comparing | → lesson |
| 10 | | | bitwise OR | left → right | → lesson | |
| 11 | && | logical AND | left → right | short-circuits — right side may never run (that's a sequence point) | → lesson |
| 12 | || | logical OR | left → right | above assignment, below &&: a || b && c is a || (b && c) | → lesson |
| 13 | ?: | conditional (ternary) | right → left | chains nest rightward: a?b:c?d:e is a?b:(c?d:e); in C the third operand can't be a bare assignment (unlike C++) | → lesson |
| 14 | = += -= *= /= %= <<= >>= &= ^= |= | assignment & compound assignment | right → left | x = y = z works because of right-assoc; c = getchar() != EOF assigns 0 or 1 to c | → lesson |
| 15 | , | comma operator — evaluate left, discard, yield right | left → right | lowest of all; a sequence point. Commas between function arguments are separators, not this operator |
| you wrote | C parsed | write instead | learn |
|---|---|---|---|
if (a & MASK == FLAG) | a & (MASK == FLAG) — the comparison runs first, then ANDs with 0 or 1 | (a & MASK) == FLAG | → lesson |
x & 1 == 0 ("is x even?") | x & (1 == 0) = x & 0 — always 0 | (x & 1) == 0 | → lesson |
*p++ | *(p++) — dereference the old p, then move the pointer | that IS the famous idiom; to bump the pointed-to value: (*p)++ | → lesson |
x << 2 + 1 | x << 3 — addition outranks shifts | (x << 2) + 1 | → lesson |
0 <= x <= 9 | (0 <= x) <= 9 — a 0/1 compared with 9: always true | 0 <= x && x <= 9 | → lesson |
while (c = getchar() != EOF) | c = (getchar() != EOF) — c is forever 0 or 1 | while ((c = getchar()) != EOF) | → lesson |
!flags == 0x4 | (!flags) == 0x4 — 0 or 1 vs 4: always false | flags != 0x4, or !(flags & 0x4) for a bit test | |
sizeof x + y | (sizeof x) + y — sizeof grabs only the nearest operand | sizeof(x + y) | → lesson |
*p.x (p is a struct pointer) | *(p.x) — dot outranks star; compile error | p->x, which is exactly (*p).x | → lesson |
a ? x = 1 : x = 2 | doesn't compile in C (it does in C++) — the third operand binds before = | x = a ? 1 : 2 | → lesson |
int x = (1, 2); | comma operator: evaluate 1, discard, x = 2 | probably not what you meant — split into statements | |
a[i, j] | a[j] — that comma is the operator, not 2-D indexing | a[i][j] | → lesson |
Precedence is not evaluation order. In f() + g() the parse is fixed but which call runs first is unspecified. And modifying a variable twice without a sequence point — i = i++ + 1, a[i] = i++ — is undefined behavior, whatever the table says. The only operators that guarantee left-before-right evaluation are &&, ||, ?: and the comma operator. Details: undefined-behavior lesson.
Memorizing the full 15-level table is a party trick, not a skill. When in doubt, add parentheses — they cost nothing and your readers (including future-you) will thank you. Historical note: & and | sit below == only for backward compatibility with pre-K&R C — Dennis Ritchie himself called it a mistake.