5.1.31C Programming

Compile-time assertions — static_assert

1,917 words9 min readdifficulty · medium1 backlinks

WHY does this exist?

The slogan: "If it's knowable at compile time, check it at compile time."


WHAT exactly is it?

Two historical forms you must recognise:

The expression must be a constant expression: built from literals, sizeof, enum values, #defined constants, etc. — anything the compiler can fully evaluate without running the program.


HOW the compiler processes it (derivation from first principles)

We don't have a "formula" to derive, but we can derive the behaviour from what a constant expression is.

  1. A constant expression EE has a value vv known at translation time (no variables, no function calls, no runtime input).
  2. The compiler computes vv.
  3. Define the rule:
compile(E)={success, emit nothingv0error: print message, stopv=0\text{compile}(E) = \begin{cases} \text{success, emit nothing} & v \neq 0\\[4pt] \text{error: print message, stop} & v = 0 \end{cases}
  1. Because step 2 happens during translation, no instruction is generated — the assertion has no footprint in the final binary. This is the whole point: it's a gatekeeper, not a runtime guard.
Figure — Compile-time assertions — static_assert

static_assert vs assert vs #error

When it checks Sees sizeof/enums? Sees runtime variables? In final binary?
#error preprocess no
static_assert compile no
assert run ✅ (and more) yes (unless NDEBUG)

Common mistakes (steel-manned)


Recall Feynman: explain it to a 12-year-old

Imagine you're building a Lego model from instructions that say "this base must be exactly 4 studs wide." A static_assert is like a smart instruction sheet that refuses to let you start building if your base isn't 4 studs wide — it stops you at setup, not after you've built the whole thing and it topples over. The check happens before the model exists (compile time), so once the model is built, the check has cost nothing and can never be skipped.


Active recall

When does static_assert evaluate its condition?
At compile time (translation), never at runtime.
What must the condition of static_assert be?
A constant integer expression (no runtime variables/function calls).
What happens if the static_assert condition is false (zero)?
Compilation fails and the message is printed as a diagnostic.
What is the raw C11 keyword behind static_assert?
_Static_assert (static_assert is a macro in <assert.h>).
How much runtime cost does a passing static_assert add?
Zero — it emits no machine code.
In C11/C17, is the message argument optional?
No, it is mandatory; in C23 it became optional.
Difference between static_assert and assert?
static_assert checks at compile time on constants; assert checks at runtime on possibly-variable values.
Why use static_assert instead of #error for sizeof checks?
#error runs in the preprocessor which can't evaluate sizeof; static_assert runs in the compiler which can.
Give a static_assert that enforces a power-of-two size N.
static_assert((N & (N-1)) == 0, "N must be power of two");
What disables assert but NOT static_assert?
Defining NDEBUG disables assert; static_assert is unaffected.

Connections

  • assert (runtime assertions)
  • Preprocessor directives — #error and #if
  • Constant expressions in C
  • sizeof operator
  • Enumerations (enum)
  • Header <assert.h>
  • C11 vs C23 language features

Concept Map

evaluates

known at

if

if

triggers

does nothing

zero footprint in binary

replaces

fires per run wasteful

motivates

macro form

native keyword

static_assert

Constant expression

Compile time check

Value is zero false

Value non-zero true

Compilation fails with message

Emits no code

Runtime assert

C11 keyword _Static_assert

C23 keyword message optional

Knowable at compile time

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho yaar, static_assert ek aisa check hai jo compile time par chalta hai, runtime par nahi. Matlab jab aap program banaa rahe ho (compile kar rahe ho), tabhi compiler dekhta hai ki aapki condition true hai ya nahi. Agar false (zero) hui, to program compile hi nahi hoga aur aapka likha hua message error ke roop mein dikhega. Iska sabse bada fayda: yeh zero runtime cost hai — final binary mein iska koi code hi nahi banta, aur isko skip karna namumkin hai.

Important baat: condition ek constant expression honi chahiye — jaise sizeof(int) == 4, ya #defined values, ya enum values. Aap isme normal variable (int n) nahi daal sakte, kyunki compiler ko answer abhi, compile ke time pe, pata hona chahiye. Agar aapko runtime value check karni hai (jaise user ka input), tab plain assert() use karo. Yaad rakho: STATIC = jo sach compile time pe hi fix hai.

C11/C17 mein static_assert actually ek macro hai jo <assert.h> se aata hai (raw keyword _Static_assert hai), aur message dena zaroori hai. C23 mein yeh proper keyword ban gaya aur message optional ho gaya. Common galti: log #error se sizeof check karne ki koshish karte hain — par #error to preprocessor mein chalta hai, usse sizeof samajh hi nahi aata. Isiliye type-size jaisi cheezein check karne ke liye static_assert perfect hai. Real life use: jab aap maan ke chalte ho ki "int 4 byte ka hai" ya "yeh array ka size enum count ke barabar hai" — toh static_assert se yeh assumption permanently lock kar do, taaki koi galat platform ya galti se badli hui value chupke se bug na bana de.

Go deeper — visual, from zero

Test yourself — C Programming

Connections