C Mastery / Precedence, Associativity, and Sequencing
Part 1 — The Core Language

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

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):

PrecedenceOperatorsAssociativity
1 (highest)() [] -> . postfix ++ --left
2prefix ++ -- + - ~ ! * & 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

unspecified).

== binds tighter than =.

Undefined Behavior

side effect and a value computation that uses that same scalar object, is undefined behavior. VERIFIED

itself, but can become UB if arguments have unsequenced side effects on the same object).

Portability

conforming compilers.

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

explicit and avoid UB.

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.

References

summary), §5.1.2.3 (sequencing).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.ops.precedence06
c.ops.sequencing06