5.1.31 · D1C Programming

Foundations — Compile-time assertions — static_assert

3,054 words14 min readBack to topic

Before you can read a single line of the parent note, you meet a pile of symbols and words: static_assert, sizeof, enum, #define, &, &&, "constant expression", "compile time", "diagnostic". This page builds each one from nothing, in the order that lets the next one stand on the previous.


0. The two clocks: compile time vs run time

Everything in this topic hinges on when something happens. So we start there.

Related runtime tool: assert (runtime assertions) lives on the right clock. static_assert is its left-clock cousin.


1. What "a value" is, and what "zero" means

The rule for static_assert is: "if the value is zero, fail." So we must nail what a value is.

So when the parent says "if the condition is zero (false), compilation fails" — it means the compiler computes one number, and only the exact value triggers the stop.


1b. Literals — the raw numbers you type

Before comparisons and sizeof, the most basic ingredient of any expression is a plain number written directly in the source. It has a name.


2. Comparison operators: how a condition becomes a number

The condition inside static_assert usually looks like sizeof(int) == 4. That == is doing real work.

So static_assert(sizeof(int) == 4, "...") really means: compute the number produced by sizeof(int) == 4; if that number is 0, stop.


3. sizeof — measuring a type with a ruler


3b. _Alignof — measuring a type's alignment

sizeof measures how big a type is. Its close cousin _Alignof measures a different compile-time fact about a type, and it shows up in the constant-expression list, so we define it now.


4. #define — a named constant the preprocessor pastes in

So by the time the compiler sees your code, BUFFER_SIZE has already become 1024. That makes it a constant the compiler can use inside a static_assert.


5. enum — auto-numbered named constants


6. Two different "AND": & (bitwise) vs && (logical)

The parent's power-of-two check (x & (x-1)) == 0 uses &. That is not the logical "and" && — they answer different questions, so we define both.


6b. The conditional operator ? : — choosing between two values

The constant-expression list also allows one more operator, ? :, which you may not have met. It is the only C operator with three parts.


7. "Constant expression" — the gatekeeper's admission rule

Now every earlier piece assembles into the one requirement that governs static_assert.


8. static_assert itself — the syntax, at last

Now that every ingredient exists, we can finally define the keyword this whole page serves.


Equipment checklist

Cover the right side and test yourself.

At which clock (compile or run) does static_assert act?
Compile time — before the program exists.
In C, which single number counts as false?
0 (zero); everything non-zero is true.
What is a literal in C?
A value written directly in the source that stands for itself, e.g. 4, 'A', "text".
What does the expression a == b evaluate to?
1 if equal, 0 if not — it becomes a number.
What does sizeof(T) return, and of what type?
The number of bytes T occupies, as an unsigned size_t.
What does _Alignof(T) return?
The required byte alignment (spacing) of type T, a compile-time size_t.
When is sizeof NOT a constant expression?
When applied to a variable-length array (VLA) — its size is a runtime value.
How do you count a fixed array's elements at compile time?
sizeof(arr) / sizeof(arr[0]).
Is a #defined name a runtime variable?
No — it is text pasted in before compiling, so it is a constant.
What pitfall does an unparenthesised #define cause?
Blind text substitution can change the arithmetic; always wrap the body in parentheses.
In enum { A = 4, B, C }, what are B and C?
B = 5 and C = 6 — they auto-increment from the pinned start value.
Difference between & and &&?
& combines numbers bit-by-bit; && asks a single yes/no "are both non-zero?".
What does cond ? a : b produce?
a if cond is non-zero, otherwise b — it yields a value.
What does x & (x - 1) == 0 test, and what must x be?
Whether x is a power of two; x must be a non-negative compile-time constant.
What is a diagnostic message in static_assert?
The string the compiler prints on failure, explaining why the build stopped.
What is the raw C11 keyword behind static_assert, and how is it used?
_Static_assert(expr, "msg") — identical usage; the macro static_assert in <assert.h> just aliases it.
Where may a static_assert be placed?
Anywhere a declaration is allowed — file scope, inside a struct, or inside a function.