The C Path — learn C, visuallyprintable cheatsheet

C operator precedence & associativity

All 15 levels, C17 · level 1 binds tightest · UB = undefined behavior

Styled for paper — hit Ctrl+P and pin it above your desk.

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.

The full table

lvloperatorswhat they areassoc.watch outlearn
1() [] . -> x++ x-- (T){…}call, index, member, arrow, postfix inc/dec, compound literalleft → rightx++ yields the old value, then bumps x→ lesson
2++x --x + - ! ~ * & (T) sizeof _Alignofprefix inc/dec, unary plus/minus, logical & bitwise NOT, dereference, address-of, cast, sizeof (C23: alignof)right → leftlevel 1 beats level 2: *p++ is *(p++), *p.x is *(p.x)→ lesson
3* / %multiply, divide, remainderleft → rightall three share the level: 2 + 3 * 4 % 5 = 2 + ((3*4) % 5) = 4→ lesson
4+ -add, subtract (binary)left → righta - b - c is (a-b)-c, not a-(b-c)→ lesson
5<< >>bit shiftsleft → rightbelow +: a << b + c is a << (b+c)→ lesson
6< <= > >=relationalleft → righta < b < c is (a<b) < c — compares a 0/1 with c, not a range test→ lesson
7== !=equalityleft → righta == b == c is (a==b) == c — same 0/1 trap→ lesson
8&bitwise ANDleft → rightbelow ==: a & b == c is a & (b==c) — the most famous trap in C→ lesson
9^bitwise XORleft → rightsame story as & — always parenthesize masks before comparing→ lesson
10|bitwise ORleft → right→ lesson
11&&logical ANDleft → rightshort-circuits — right side may never run (that's a sequence point)→ lesson
12||logical ORleft → rightabove assignment, below &&: a || b && c is a || (b && c)→ lesson
13?:conditional (ternary)right → leftchains 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 assignmentright → leftx = y = z works because of right-assoc; c = getchar() != EOF assigns 0 or 1 to c→ lesson
15,comma operator — evaluate left, discard, yield rightleft → rightlowest of all; a sequence point. Commas between function arguments are separators, not this operator

The classic traps, spelled out

you wroteC parsedwrite insteadlearn
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 pointerthat IS the famous idiom; to bump the pointed-to value: (*p)++→ lesson
x << 2 + 1x << 3 — addition outranks shifts(x << 2) + 1→ lesson
0 <= x <= 9(0 <= x) <= 9 — a 0/1 compared with 9: always true0 <= x && x <= 9→ lesson
while (c = getchar() != EOF)c = (getchar() != EOF) — c is forever 0 or 1while ((c = getchar()) != EOF)→ lesson
!flags == 0x4(!flags) == 0x4 — 0 or 1 vs 4: always falseflags != 0x4, or !(flags & 0x4) for a bit test
sizeof x + y(sizeof x) + y — sizeof grabs only the nearest operandsizeof(x + y)→ lesson
*p.x (p is a struct pointer)*(p.x) — dot outranks star; compile errorp->x, which is exactly (*p).x→ lesson
a ? x = 1 : x = 2doesn'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 = 2probably not what you meant — split into statements
a[i, j]a[j] — that comma is the operator, not 2-D indexinga[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.