Precedence, Associativity, and Sequencing
This chapter covers the three rules that determine how an expression is parsed and evaluated: precedence, associativity, and sequencing. These are distinct concepts that are frequently confused.
Why This Matters
Precedence tells you how the compiler groups subexpressions. Associativity tells you how equal-precedence operators group. Sequencing tells you the order in which evaluations happen. Confusing these — especially assuming precedence implies evaluation order — leads directly to undefined behavior.
Prerequisites
c.core.9— arithmetic, comparison, logical operators.c.core.10— bitwise and shift operators.c.core.11— assignment, compound assignment, increment/decrement.c.core.8— expressions and evaluation.
Core Concept
Precedence
Precedence determines how operators bind to their operands when multiple operators appear. Higher precedence binds more tightly.
a + b * c /* parses as a + (b * c), because * binds tighter than + */
Associativity
Associativity determines how operators of the same precedence group:
a - b - c /* left-associative: (a - b) - c */
a = b = c /* right-associative: a = (b = c) */
Most binary operators are left-associative. Assignment and the conditional operator are right-associative.
Sequencing
Sequencing determines the temporal order of evaluations and side effects. Two evaluations are sequenced if one happens before the other. If neither is sequenced before the other, they are unsequenced (and if they both affect the same scalar object, the program has undefined behavior).
There is a sequence point (a point where all previous side effects are complete and no later side effects have started) between certain evaluations, most notably after each full expression.
The Full Precedence Table
From highest to lowest (C17):
| Precedence | Operators | Associativity | ||
|---|---|---|---|---|
| 1 (highest) | () [] -> . postfix ++ -- | left | ||
| 2 | prefix ++ -- + - ~ ! * & sizeof _Alignof (type) | right | ||
| 3 | * / % | left | ||
| 4 | + - | left | ||
| 5 | << >> | left | ||
| 6 | < <= > >= | left | ||
| 7 | == != | left | ||
| 8 | & | left | ||
| 9 | ^ | left | ||
| 10 | ` | ` | left | |
| 11 | && | left | ||
| 12 | ` | ` | left | |
| 13 | ?: | right | ||
| 14 | = += -= *= /= %= <<= >>= &= ^= ` | =` | right | |
| 15 (lowest) | , | left |
VERIFIED
Examples
Precedence vs. associativity
int a = 1, b = 2, c = 3;
int x = a + b * c; /* 7, not 9 */
int y = a - b - c; /* (1 - 2) - 3 = -4 */
int z = a = b = c; /* z = (a = (b = c)) => 3 */
The famous comma vs. parentheses example
int x = 1, 2, 3; /* ERROR: comma separates declarators, not the operator */
int y = (1, 2, 3); /* y = 3 (comma operator) */
Sequencing pitfalls
int i = 0;
int a = i++ + ++i; /* UB: unsequenced side effects on i */
How It Works
The compiler first *parses* the expression using precedence and associativity to build an abstract syntax tree. It then applies *sequencing* rules to decide evaluation order. Precedence is about parsing; sequencing is about evaluation order. They are independent.
Variations
Sequence points (historical vs. modern wording)
Older C (C89/C90) used "sequence points" as the primary concept. C11/C17 refined this to "sequenced before / sequenced after / unsequenced / indeterminately sequenced" relations, but "sequence point" is still used for full expressions and certain library calls.
Function arguments are indeterminately sequenced
The order of evaluation of function arguments is unspecified (and the evaluations are not sequenced relative to each other). This is a common source of bugs.
Common Mistakes
- Believing precedence determines evaluation order. It does not.
- Assuming function arguments evaluate left to right. They do not (the order is
unspecified).
- Writing
i++ + i++or similar unsequenced modifications. - Misreading
a = b == cas(a = b) == c; it isa = (b == c)because
== binds tighter than =.
Undefined Behavior
- Two unsequenced side effects on the same scalar object, or an unsequenced
side effect and a value computation that uses that same scalar object, is undefined behavior. VERIFIED
- The order of evaluation of function arguments is unspecified (not UB by
itself, but can become UB if arguments have unsequenced side effects on the same object).
Portability
- Precedence and associativity are standard and identical across all
conforming compilers.
- Evaluation order (where unspecified) may differ between compilers and even
between optimization levels.
Under the Hood
The compiler's parser uses an operator-precedence grammar or an equivalent to build the AST. Sequencing rules are used by the optimizer to determine which reorderings are legal. An optimizer cannot reorder two operations across a sequence point if doing so would change observable behavior.
Practical Usage
- When in doubt, add parentheses. They are cheap and clarify intent.
- Break complex expressions into multiple statements to make sequencing
explicit and avoid UB.
- Avoid side effects in expressions that share an object across sequence
points.
Exercises
1. Compute the parse of each expression by hand, then verify with gcc -E or a small program: 1 + 2 * 3, (1 + 2) * 3, a = b = 5, 1 < 2 == 1. 2. Write a program that demonstrates function argument evaluation order is unspecified (using a function with side effects in arguments). 3. Explain why i = i++ and i++ + i++ are UB, citing the rule.
Deep Challenge
Using the C17 sequencing definitions, determine whether each expression is well-defined, and if so, its value (assume int i = 0; initially):
i = i + 1;
i += 1;
i++ + 1;
i++ + i; /* ? */
f(i++, i++); /* ? */
Explain each answer precisely.
Related Concepts
c.core.8— expressions and evaluation.c.ub.definedness— definedness classification.c.types.uac— conversions that interact with expression types.
References
- ISO/IEC 9899:2018 §6.5 (expressions, precedence), Annex A (grammar
summary), §5.1.2.3 (sequencing).
Verification
- The precedence table matches ISO C17 Annex A.
VERIFIED - Unsequenced side-effect UB rule.
VERIFIED - Function argument evaluation order is unspecified.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Precedence
- [ ] Associativity
- [ ] Sequencing and sequence points
- [ ] Unsequenced side-effect UB
- [ ] Function argument evaluation order
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.ops.precedence | 0 | 6 |
| c.ops.sequencing | 0 | 6 |