5.1.31 · D3C Programming

Worked examples — Compile-time assertions — static_assert

3,080 words14 min readBack to topic

You have met the parent note and know the one-line rule: static_assert(condition, "message") makes the compiler refuse to build if condition evaluates to zero at compile time. This page does the opposite of theory — it grinds through every kind of case the tool can throw at you, so that when you hit one in the wild you have already seen its twin.

Before any example: the condition is a constant expression (see Constant expressions in C). That means the compiler can compute a single fixed number for it — call that number without running the program. Then:


The scenario matrix

Think of static_assert as a machine that eats a constant and outputs pass or fail. The "cases" are all the different shapes that constant can take. Here is every cell we will cover.

Cell Case class Representative question
A Truthy value (any non-zero → pass) sizeof(int) == 4 on a 32-bit box
B Falsy value (exactly zero → fail) same check on a 16-bit box
C Degenerate: bare non-zero literal static_assert(1) — does it even pass?
D Zero-producing arithmetic power-of-two mask x & (x-1)
E Two things kept in sync array length vs enum count
F Boundary / limiting value smallest legal size, x = 1, x = 0
G Negative & signedness twist -1 is truthy; unsigned wrap
H Real-world word problem packet header must fit a fixed wire size
I Exam twist which of these is not a constant expression?

Each example below is tagged with the cell(s) it covers. Together they touch every row.


Cell A + C — truthy values and the degenerate literal


Cell B — the falsy value that stops the build

Figure — Compile-time assertions — static_assert

Cell D + F — arithmetic that produces zero, and the boundary values

The classic "must be a power of two" test uses the mask , where & is bitwise AND. Here is why it works, in binary, before we assert with it.

Figure — Compile-time assertions — static_assert

Cell E — keeping two definitions in sync


Cell G — negatives and the signedness twist


Cell H — the real-world word problem


Cell I — the exam twist: what is not a constant expression?


Coverage check — did we hit every cell?

Recall Matrix cells vs examples (all nine confirmed)
  • A truthy value → Example 1 (1, 42, sizeof(char) all non-zero)
  • B falsy value → Example 2 (sizeof(int)==4 gives on a 16-bit box)
  • C bare non-zero literal → Example 1 (static_assert(1) passes)
  • D zero-producing arithmetic → Example 3 (power-of-two mask)
  • E two things in sync → Example 4 (array length vs COLOR_COUNT)
  • F boundary / degenerate → Example 3 (the and cases)
  • G negative & signedness twist → Example 5 (-1 truthy; unsigned wrap)
  • H real-world word problem → Example 6 (8-byte packet header)
  • I exam twist → Example 7 (which condition is not a constant expression)

All nine cells A–I are covered — no scenario left unshown.

Active recall

Does static_assert(-1) pass?
Yes — is truthy in C.
Does (x & (x-1)) == 0 correctly reject x = 0?
No — it is true for , so add x != 0.
Is sizeof(n) == 4 a valid static_assert even if n is a variable?
Yes — sizeof uses the type, resolved at compile time.
Why is get_count() == 4 illegal in static_assert?
A function call is a runtime value, not a constant expression.
For 1000, what is 1000 & 999?
992 (non-zero), so the power-of-two assert fails.
Which header gives you the static_assert spelling in C11/C17?
<assert.h> (or use the raw _Static_assert keyword with no include).