CAssertions (assert.h)

Assertions (assert.h)

The <assert.h> header provides a single macro, assert(condition), used to catch programmer errors — bugs that should be logically impossible if the code is correct — as early and loudly as possible during development.

How assert() Works

If condition evaluates to false (zero), assert prints a diagnostic message (including the failing expression, file name, and line number) to stderr and calls abort(), immediately terminating the program. If condition is true, assert does nothing at all — no overhead beyond the check itself.

C
#include <stdio.h>
#include <assert.h>

double divide(double a, double b) {
    assert(b != 0.0); /* the caller must never pass zero -- that's a bug, not a runtime error */
    return a / b;
}

int main(void) {
    printf("10 / 2 = %f\n", divide(10.0, 2.0));

    /* This next call violates the function's contract and will
       trigger the assertion, aborting the program with a diagnostic:
       "Assertion failed: b != 0.0, file example.c, line 6" */
    printf("10 / 0 = %f\n", divide(10.0, 0.0));

    return 0;
}
Assertions Are Compiled Out with NDEBUG

Defining the macro NDEBUG before including <assert.h> (or passing -DNDEBUG to the compiler) makes every assert(...) expand to nothing — the checks, and any side effects inside them, simply vanish from the compiled program. This is the standard way to strip assertion overhead from release builds while keeping the checks active during development and testing.

Bash
# Debug build: assertions are active
gcc -g program.c -o program_debug

# Release build: assertions are compiled away entirely
gcc -O2 -DNDEBUG program.c -o program_release
Warning
Never put code with **necessary side effects** inside `assert(...)`. Because assertions disappear entirely under `NDEBUG`, any side effect inside them disappears too — a classic and painful bug:
`assert(list_remove(list, item) == 0);` — in a release build, `list_remove` never runs at all, silently changing your program's behavior.
Assertions vs Proper Error Handling

Assertions and error handling solve different problems and should not be used interchangeably. Assertions are for conditions that indicate a bug in the program itself — situations that should be logically impossible if all the code is correct. Proper error handling (return codes, errno, etc.) is for conditions that can legitimately happen at runtime even in a perfectly correct program — a missing file, a failed network connection, invalid user input.

Scenario

Use assert()?

Reasoning

A function argument violates its documented precondition

Yes

Calling code has a bug

A file the user asked to open does not exist

No

Expected, recoverable runtime condition

An internal invariant should always hold (e.g. array index in bounds after your own math)

Yes

If it fails, the logic itself is wrong

malloc() returns NULL

No

A real possible runtime failure — handle it, do not assert it away

User typed invalid input on stdin

No

Expected user error, not a programmer bug

A Practical Example

C
#include <stdio.h>
#include <assert.h>

/* Precondition: index must be within [0, size). This is a contract
   the CALLER is responsible for upholding -- violating it is a bug. */
int getElement(const int *arr, int size, int index) {
    assert(index >= 0 && index < size);
    return arr[index];
}

int main(void) {
    int data[5] = { 10, 20, 30, 40, 50 };

    printf("%d\n", getElement(data, 5, 2)); /* fine */

    /* Uncommenting the next line triggers the assertion because
       index 10 is out of bounds -- a programmer error, caught early. */
    /* printf("%d\n", getElement(data, 5, 10)); */

    return 0;
}
Note
Think of assertions as executable documentation of your assumptions. When an assertion fails during testing, it points precisely at the file and line where an assumption you thought was guaranteed turned out to be false — far easier to debug than chasing the corrupted state that assumption's violation would otherwise cause several function calls later.
Tip
A useful mental test: "if this condition is false, is it because the **caller of this code** made a mistake, or because the **outside world** (files, users, networks) did something unexpected?" The former is a job for `assert`; the latter is a job for proper error handling.