5.1.5 · D1C Programming

Foundations — Operator precedence — full table

3,063 words14 min readBack to topic

Before you can read the full precedence table, you need to genuinely understand the tiny pieces it is built from. This page defines every symbol and idea the parent assumes — starting from "what is an operator?" — so that no line of the parent note is a mystery.


1 — What is an expression?

The picture: think of an expression as a little machine with an output slot. You feed it in, a number falls out the bottom.

Figure — Operator precedence — full table

(Figure 1 — a rounded box labelled a + 3 with an arrow feeding a in from the left and a value 8 dropping out of a slot at the bottom: an expression takes inputs and outputs exactly one value.)

Why the topic needs this: the whole precedence table is a set of rules about how to combine smaller expressions into bigger ones. If you don't see that b * c is itself a value-producing machine that plugs into a + (…), none of the grouping talk makes sense. This is the ground floor — see Expressions and Statements in C.


2 — What is an operator and an operand?

The picture: an operator is a socket with two prongs (for most operators). Each prong plugs into an operand — and that operand may itself be another whole machine that first has to output a value.

Figure — Operator precedence — full table

(Figure 2 — the flat text a + b * c; teal arrows show + reaching for a and b, orange arrows show * reaching for b and c. The letter b is pulled from both sides — a tug-of-war that precedence must settle.)

Counting prongs (arity): most operators take two operands (a + b) — call them binary. Some take one (-a, !x, a++) — call them unary. One takes three (a ? b : c) — the ternary. You will meet all three arities in the table, so we name them now.

Reveal check:

How many operands does a unary operator take?
One (e.g. -a or !x).
Is an operand always a bare number?
No — an operand is any sub-expression that produces a value, so it can be a whole grouped expression like (b * c).

3 — What is a parse tree?

The parent note keeps saying the compiler builds "a unique parse tree." Here is what that literally is.

The picture: it is an upside-down family tree. The leaves at the bottom are your raw values; each operator up the tree waits for its children to hand it their values, then produces its own.

Figure — Operator precedence — full table

(Figure 3 — the parse tree of a + b * c: a top + node with a on the left and a * node on the right, and the * node has children b and c. The tree shows only the SHAPE, i.e. that b*c groups together — it says nothing about which branch the CPU computes first in time.)

Why the topic needs this: "precedence decides the tree shape" is the single most important sentence on the parent page. Once you can see the tree, you understand that a + b * c and (a + b) * c are two different trees — the whole point of the rules is to pick one and only one.


4 — Precedence: "who is stronger?"

Back to the tug-of-war: in a + b * c, the * is stronger than +, so * wins b. Result: a + (b * c). The stronger operator ends up lower in the tree, because it forms the inner group that gets built first.

Reveal check:

In a + b * c, which operator ends up lower in the parse tree?
*, because it binds tighter (level 3 binds tighter than level 4), so it grabs b and c first.
Does "lower in the tree" mean "computed first in time"?
No — it means grouped more tightly; real-time order is decided by sequence points, not precedence.

5 — Associativity: the tie-breaker

Precedence only helps when operators differ in strength. What about a - b - c? Both are - (same level 4). Precedence is silent. We need a second rule.

The picture: imagine the operators reaching for the shared middle operand. L→R means the left one grabs first; R→L means the right one grabs first.

Figure — Operator precedence — full table

(Figure 4 — two panels. Left: a - b - c with a teal left-pointing grab showing the left - binds first → (a - b) - c. Right: a = b = 0 with a plum right-pointing grab showing the right = binds first → a = (b = 0). L→R is the default; R→L is the rare exception.)

Why the topic needs this: without associativity, a - b - c would be ambiguous even after precedence. And it matters: (a - b) - c and a - (b - c) give different answers for subtraction. Assignment and the ternary (see The Ternary Conditional Operator) are the two famous right-to-left cases the parent highlights.

Reveal check:

Why can't precedence alone group a - b - c?
Both - operators have equal precedence, so we need associativity (L→R) to break the tie.
What is the default associativity for most C operators?
Left-to-right; only the unary group, assignment, and the ternary ?: are right-to-left.

6 — The parentheses ( ) — the override switch

The picture: parentheses are a hard box drawn around part of the expression — nothing outside can reach inside until the box has produced its single value.

Why the topic needs this: every "fix" in the parent's worked examples is the same move — add parentheses to reshape the tree. if ((x & 1) == 0) forces x & 1 to be its own box before == can run.


7 — The top of the table: postfix and unary operators

The very tightest-binding operators (levels 1 and 2) rarely get discussed, but they change tree shape decisively. You must recognise them.

Reveal check:

Why does -a++ mean -(a++)?
Postfix ++ is level 1 (tightest), unary - is level 2, so ++ grabs a first.
Is ~ (bitwise NOT) left- or right-to-left?
Right-to-left — it is a level-2 unary operator.

8 — The families of operators you'll meet

You don't need to master these yet — you just need to recognise the categories so the table isn't a wall of strange symbols. (Levels 1–2 above; the rest here.)

Symbol(s) Family Plain meaning Assoc Go deeper
+ - * / % arithmetic ordinary maths; % = remainder L→R Expressions and Statements in C
<< >> bit shift slide bits left/right L→R Bitwise Operators
< <= > >= == != relational / equality comparisons; give 1 (true) or 0 (false) L→R
& ^ | bitwise combine bits: AND, XOR, OR L→R Bitwise Operators
&& || logical true/false logic with short-circuit L→R Logical Operators and Short-circuit Evaluation
?: ternary inline if/else R→L The Ternary Conditional Operator
= += ... assignment store a value into a variable R→L
, comma do this, then that; value is the last L→R

Reveal check:

What does % mean in C?
The remainder after integer division (e.g. 7 % 3 = 1).

9 — "True" and "false" in C

The relational and logical operators produce truth values, but C has no separate boolean picture — it uses numbers.

Why the topic needs this: the parent's bug if (a = 5) only makes sense once you know a = 5 produces the value 5, which is non-zero, hence true — the if always fires. And the bit-mask trap x & (1 == 0) reduces to x & 0 because 1 == 0 is false, i.e. 0.


Prerequisite map

The diagram below reads top to bottom: a raw value and an operator + operand pair both feed the idea of an expression; expressions plus arity build a parse tree; the tree needs precedence (strength) and associativity (tie-break) to become unique; those two, together with parentheses, the truthiness rule, and the operator families, are exactly what the full precedence table assembles.

A value

Expression produces a value

Operator plus operand

Parse tree shows grouping

Arity unary binary ternary

Precedence who binds tighter

Associativity tie breaker

The full precedence table

Parentheses override

Zero is false nonzero is true

Operator families


Equipment checklist

Self-test: can you answer each before revealing?

An expression is
any piece of code that produces a value (even b = 5 produces 5).
An operator vs an operand
the operator is the combining symbol (+); the operands are the sub-expressions it works on — each itself producing a value.
Can an operand be a whole sub-expression
yes — an operand is any value-producing sub-expression, e.g. (b * c), not just a bare literal.
Arity means
how many operands an operator takes — unary (1), binary (2), ternary (3).
A parse tree shows
what groups with what — the unique shape of the expression, not the timing.
Precedence answers
which operator binds tighter and grabs its operands first (a smaller level number binds tighter).
"Lower in the tree" means
grouped more tightly — NOT necessarily computed first in time; timing is set by sequence points.
Associativity answers
for equal-precedence operators, do we group left-to-right or right-to-left.
The default associativity is
left-to-right; only the unary group, assignment, and the ternary ?: are right-to-left.
Level-1 operators include
() call, [] subscript, . and -> member access, and postfix ++ -- — they bind tightest of all.
Level-2 unary operators include
prefix ++ --, unary + -, !, ~, * deref, & address-of, sizeof, (type) cast — all right-to-left.
Parentheses ( ) do what to the rules
override everything — they force a subtree (level 1, top of the table).
In C, false and true are
0 is false; any non-zero value is true.
& vs &&
& is bitwise (bit-by-bit); && is logical (true/false, short-circuits) — different operators, different levels.

Connections

  • Operator precedence — full table (index 5.1.5)
  • Expressions and Statements in C
  • Bitwise Operators
  • Logical Operators and Short-circuit Evaluation
  • Sequence Points and Undefined Behaviour
  • Type Casting and Conversions
  • The Ternary Conditional Operator