If you have ever had a function produce a wrong result somewhere deep in a call stack with no indication of where things went wrong, assert.h is what you reach for. It provides the assert macro, which checks a condition at runtime and immediately kills the program with a diagnostic message that tells you exactly which file, which line, and which condition failed.
#include <assert.h> int divide(int a, int b) { assert(b != 0); // if false: prints file, line, expression, then abort() return a / b; }
When the condition is false, you get:
Assertion failed: b != 0, file math.c, line 4
…then abort(). You get a crash at the exact line, not some mysterious wrong value three function calls downstream.
Assertions are stripped out entirely when you compile with -DNDEBUG. The preprocessor removes them completely, so they cost nothing in release builds. Because of this, you must never give an assertion a side effect — assert(read_sensor() > 0) would skip the sensor read in production, which is almost certainly not what you want.
C11 added static_assert, which is evaluated at compile time:
static_assert(sizeof(int) == 4, "int must be 32 bits on this platform");
This fires before a single line of your program runs, at compile time. Useful for catching platform assumption mismatches early.
// compile: gcc -o div div.c // run: ./div // description: assertion fires on bad input; rebuild with -DNDEBUG to strip it #include <assert.h> #include <stdio.h> int divide(int a, int b) { assert(b != 0); return a / b; } int main(void) { printf("%d\n", divide(10, 2)); // fine: prints 5 printf("%d\n", divide(10, 0)); // triggers the assertion return 0; }
Run it and you will see 5 printed, then the assertion fires before the second call returns. Now rebuild with -DNDEBUG:
gcc -DNDEBUG -o div div.c && ./div
The assertion is gone. The divide-by-zero silently produces garbage (or a SIGFPE, depending on the platform). That is the point: assertions are for catching bugs during development, not for validating inputs in production.