5.1.31 · D5C Programming

Question bank — Compile-time assertions — static_assert

1,314 words6 min readBack to topic

Before you start, remember the one sentence that resolves most of these: a static_assert is evaluated by the compiler, so its condition must be a constant expression — a value fully known without running the program.


True or false — justify

C11 lets you omit the message string in static_assert.
False. In C11/C17 the message is mandatory; only C23 (see C11 vs C23 language features) made it optional. Forgetting it under C11 is a hard compile error.
A passing static_assert adds a tiny runtime check that the CPU skips quickly.
False. A passing assertion emits zero machine code — there is nothing at runtime to skip. The compiler already computed the value during translation.
static_assert(1 == 1); and assert(1 == 1); are basically the same thing.
False. static_assert fires at compile time and needs a constant; assert (from <assert.h>) fires at run time and can inspect variables. They live in different phases of the program's life.
You can write static_assert in C11 without including any header.
False for the friendly name — in C11 static_assert is a macro in <assert.h>. The raw keyword _Static_assert needs no header at all.
#error can check whether sizeof(int) == 4.
False. #error runs in the preprocessor, which has no type information — it cannot evaluate sizeof. Only the compiler (via static_assert) knows type sizes.
static_assert(sizeof(int) == 4); will compile fine as C23.
True. In C23 static_assert is a real keyword and the message is optional, so a lone condition is legal. Under C11 the same line fails for lack of a message.
Compiling out assert with NDEBUG also disables static_assert.
False. NDEBUG only silences the runtime assert. static_assert is unaffected — it is not a debugging aid you can turn off, it is a build gate.
A false static_assert inside a function you never call is harmless.
False. The compiler evaluates it during translation regardless of whether the enclosing function is ever called or reachable — the build still fails.

Spot the error

int n = read_config();
static_assert(n > 0, "n must be positive");

::: n is a runtime variable, so n > 0 is not a constant expression — this fails to compile. For runtime data use assert instead; static_assert is only for compile-time-known values.

// C11 source, no includes
static_assert(sizeof(long) >= 4, "long too small");

::: In C11 the spelling static_assert is a macro — without #include <assert.h> the name is undefined. Fix: include the header, or write the raw keyword _Static_assert.

#define SIZE 100
#error (SIZE % 2 == 0) ? 0 : "SIZE must be even"

::: #error does not evaluate expressions — everything after it is just text printed unconditionally. To conditionally check an even size you need #if SIZE % 2 != 0#error …, or a static_assert in code.

static_assert(strlen("hi") == 2, "wrong");

::: strlen is a runtime function call, so the argument is not a constant expression — it won't compile. Use sizeof("hi") - 1 (a compile-time constant, minus the null terminator) if you truly need it at build time.

static_assert(get_flag() ? 1 : 0, "flag off");

::: A function call get_flag() can never be a constant expression in C, so this is illegal. Constant conditions must be built from literals, sizeof, enum values, and #defined constants only.

enum { A = 1, B = 2 };
static_assert(A + B, "sum is zero");   // meant to catch A+B == 0

::: The condition is just the value 3, which is non-zero (true), so it silently passes and checks nothing meaningful. The author forgot the comparison; they wanted static_assert(A + B != 0, ...).


Why questions

Why must the condition be a constant, not just "any true expression"?
Because the compiler must evaluate it during translation with no program running — a variable's value simply doesn't exist yet at compile time, so there is nothing for the compiler to test.
Why does static_assert cost zero runtime while assert costs a little?
static_assert is fully resolved at compile time and produces no code; assert compiles into an actual runtime if-check-and-abort that executes each time the line runs (unless removed by NDEBUG).
Why prefer static_assert(sizeof(int)==4) over a runtime check for the same thing?
The size of int is fixed the instant you compile — it never varies per run or per input. A runtime check would waste cycles re-verifying a truth already known, and could ship in a broken binary; the compile-time check makes the broken build impossible to produce.
Why can static_assert use sizeof but #error cannot?
sizeof needs the compiler's knowledge of types; #error acts in the preprocessor, an earlier phase that only manipulates text tokens and knows nothing about types or sizes.
Why is static_assert(sizeof(names)/sizeof(names[0]) == COUNT) a constant expression?
An array's total byte size and element size are both fixed at compile time, so their quotient (the element count) is a compile-time constant — the compiler can compare it to COUNT without running anything.
Why does putting a static_assert at file scope (outside any function) work?
static_assert is a declaration, so it is legal anywhere a declaration is — at file scope, inside a struct, or inside a function body. It needs no surrounding statement or executable context.

Edge cases

static_assert(0, "always here"); — what happens?
The condition is constant zero (false), so compilation always fails and prints the message. This is a common trick to unconditionally reject an unsupported build configuration.
static_assert(-1, "check"); — passes or fails?
Passes. The rule is "fails only if the value is zero"; any non-zero value, including negatives, counts as true. Only 0 triggers the diagnostic.
static_assert(0.0 == 0.0, "float check"); in C11 — legal?
Problematic. C11 requires a constant integer expression; a floating comparison yields an int (1) here so it may pass, but relying on floating-point constants is fragile and non-portable — keep conditions integer-valued.
Can two static_asserts at the same scope both be false, and which message shows?
Both are evaluated; a conforming compiler reports each failing assertion's message (implementations may stop early, but the build fails regardless). Neither silently overrides the other.
A static_assert inside a struct with a false condition — does it still fire?
Yes. Since C11, static_assert may appear among struct members and is checked when the type is compiled — the build fails even though no object of that struct exists yet.
static_assert(sizeof(char) == 1); — is this ever false?
No — sizeof(char) is 1 by definition in C, on every platform. So this particular assertion can never fail; it is a tautology, useful only as documentation of intent, not as a real guard.
What if the enum value in the condition is defined after the static_assert in the file?
It fails to compile because the name is not yet declared at that point. Constant expressions must reference names already in scope — order matters, just like any other declaration.