C Standard Library assert.h

Beginner
⏱️ 9 min read
📚 Updated: Jul 2026
🎯 5 Examples
<assert.h>

What You’ll Learn

The <assert.h> header provides the assert macro—a simple debugging tool that verifies assumptions in your code. When a check fails, you get a clear error message and the program stops immediately so you can fix the bug early.

01

assert

Debug macro.

02

assert.h

One header.

03

NDEBUG

Disable in release.

04

abort()

On failure.

05

Invariants

Internal checks.

06

Not users

Dev-only tool.

Definition and Usage

assert(expression) tests whether expression is non-zero (true). If it evaluates to zero (false), the implementation prints a diagnostic to stderr (typically including the expression, file name, and line number) and calls abort() to terminate the program.

Use assert to document assumptions that must hold if your code is correct—non-null pointers after allocation, valid array indices, preconditions on function arguments. It is a development aid, not a substitute for handling recoverable errors from users or the OS.

💡
Beginner Tip

assert is a macro, not a function. Never put side effects inside assert (like assert(i++))—when you compile with -DNDEBUG, that code may disappear entirely.

📝 Syntax

Include the header and use the macro:

C
#include <assert.h>

assert(expression);

Macro defined in assert.h

  • assert(expression) — if expression is false (zero), print diagnostic and call abort(). If true (non-zero), no effect.

Disabling assertions

C
#define NDEBUG          /* define before #include <assert.h> */
#include <assert.h>

/* Or compile: gcc -DNDEBUG ... */

Parameters

  • expression — any scalar expression with a value comparable to zero. Common forms: ptr != NULL, n >= 0, index < size.

Headers and linking

  • #include <assert.h>
  • No special link flag required (unlike math.h).
  • Compile: gcc program.c -std=c11 -o program

⚡ Quick Reference

PatternMeaningOn failure
assert(ptr != NULL)Pointer must be validabort + message
assert(n >= 0)Non-negative countabort + message
assert(i < len)Index in boundsabort + message
#define NDEBUGStrip all assertsno-op
static_assert(...)Compile-time check (C11)build error
Include
#include <assert.h>

Standard header

Check
assert(condition)

Runtime macro

Release
-DNDEBUG

Remove asserts

Not for
user errors

Use return codes

Examples Gallery

Compile debug examples without NDEBUG to see assertions fire. Use gcc file.c -std=c11 -g -o out for clear line numbers in failure messages.

📚 Getting Started

Catch invalid arguments before they cause silent bugs.

Example 1 — Assert Non-Negative Input for sqrt()

Classic pattern from the reference: guard a function precondition.

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

double calculate_sqrt(double number) {
    assert(number >= 0);
    return sqrt(number);
}

int main(void) {
    printf("sqrt(25) = %.2f\n", calculate_sqrt(25.0));
    printf("sqrt(-4) = %.2f\n", calculate_sqrt(-4.0));

    return 0;
}

How It Works

The first call succeeds. The second passes a negative value; assert fails, prints the expression and location, and aborts before sqrt receives invalid input. Link with -lm if needed.

Example 2 — Assert Pointer Is Not NULL

Document that a pointer argument must be valid before dereferencing.

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

void print_length(const char *text) {
    assert(text != NULL);
    printf("Length check: first char is '%c'\n", text[0]);
}

int main(void) {
    print_length("hello");
    print_length(NULL);

    return 0;
}

How It Works

Without assert, dereferencing NULL causes undefined behavior. The assertion fails fast with a readable message during development.

📈 Practical Patterns

Bounds checks, release builds, and proper error handling.

Example 3 — Array Index Bounds Check

Verify an index is inside the array before access.

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

int get_item(const int *arr, size_t len, size_t index) {
    assert(arr != NULL);
    assert(index < len);
    return arr[index];
}

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

    printf("data[1] = %d\n", get_item(data, 3, 1));
    printf("data[5] = %d\n", get_item(data, 3, 5));

    return 0;
}

How It Works

Index 5 is out of range for length 3. assert catches the programmer error before memory corruption occurs.

Example 4 — Disabling assert with NDEBUG

Same code compiles differently for debug vs release.

C
/* Debug build:   gcc assert_ndebug.c -std=c11 -g -o debug */
/* Release build: gcc -DNDEBUG assert_ndebug.c -std=c11 -O2 -o release */

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

int main(void) {
    int secret = 0;

    assert((secret = 42));  /* side effect — avoid this pattern! */
    printf("secret = %d\n", secret);

    return 0;
}

How It Works

With NDEBUG, assert(...) vanishes. The assignment inside assert does not run—another reason never to put side effects in assert expressions.

Example 5 — assert vs Proper Error Handling

Use return codes for recoverable user errors; reserve assert for internal logic.

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

int parse_positive(int n) {
    if (n <= 0) {
        return -1;   /* user gave bad input — recoverable */
    }
    assert(n > 0);   /* internal invariant after the check */
    return n * 2;
}

int main(void) {
    int result;

    result = parse_positive(5);
    printf("parse_positive(5)  = %d\n", result);

    result = parse_positive(-1);
    printf("parse_positive(-1) = %d (error code)\n", result);

    return 0;
}

How It Works

Invalid user input returns -1 without crashing. assert documents that if execution reaches past the if, n must be positive—a guarantee for maintainers and debug builds.

🚀 Common Use Cases

  • Function preconditions — arguments must satisfy contracts (non-null, in range).
  • Postconditions — result of a function meets expectations before return.
  • Loop invariants — index stays within bounds each iteration during development.
  • Unreachable codeassert(0 && "should not reach here") after a full switch.
  • Unit testing aid — catch logic errors early while writing tests.
  • Documentation — assertions spell out assumptions readers might miss.

🧠 How assert() Works

1

Evaluate expression

The macro evaluates expression once (when not disabled).

Check
2

Non-zero?

If true, execution continues with no effect.

Pass
3

Print diagnostic

On failure: message to stderr with expression, file, line.

Fail
=

abort()

Program terminates immediately. Use a debugger to inspect the failure site.

📝 Notes

  • assert is a macro, not a function—no guaranteed stack frame or single evaluation when NDEBUG is set.
  • Define NDEBUG before #include <assert.h> to disable all assertions.
  • Failed assert calls abort()—not recoverable; unsuitable for user input errors.
  • Do not put side effects inside assert(expression).
  • static_assert (C11) is separate—compile-time only, keyword not in assert.h.
  • Assertion messages and exact format vary slightly by compiler (GCC, Clang, MSVC).

⚡ Optimization

In debug builds, assert adds a branch and possibly string data at each site. That is acceptable during development. For release builds, compile with -DNDEBUG (or -O2 projects that define it) so assertions compile out entirely—zero runtime cost.

Avoid dense assert calls in inner loops on hot paths even in debug builds; prefer checking invariants at function entry or less frequently.

Conclusion

<assert.h> gives you a lightweight way to catch logic errors during development. The assert macro documents assumptions, fails loudly with file and line information, and strips cleanly from release builds via NDEBUG.

Use it for internal invariants, not user-facing validation. Pair debug assertions with proper error returns for production robustness.

💡 Best Practices

✅ Do

  • Use assert for programmer errors and impossible states
  • Strip asserts in release with -DNDEBUG
  • Write clear expressions: assert(ptr != NULL)
  • Combine with return codes for user-facing validation
  • Run debug builds with assertions during testing

❌ Don’t

  • Put side effects inside assert(...)
  • Assert on untrusted user or network input
  • Rely on assert for security checks in production
  • Assume assert is a function you can rely on at runtime in release
  • Confuse assert with static_assert

Key Takeaways

Knowledge Unlocked

Five things to remember about assert.h

Debug assertions in C, explained simply.

5
Core concepts
🛑 02

abort()

On failure.

Behavior
📈 03

NDEBUG

Strip asserts.

Release
📄 04

Invariants

Dev checks.

Purpose
🔄 05

Return codes

User errors.

Production

❓ Frequently Asked Questions

assert.h is a standard header that defines the assert macro. assert(expression) checks that expression is true during development. If it is false (zero), the program prints a diagnostic message to stderr and calls abort() to terminate.
assert is a macro, not a function. The standard shows it as assert(expression) but it expands to code that may include __FILE__ and __LINE__. Do not rely on assert having function-call semantics (e.g. side effects may be skipped when NDEBUG is defined).
Define NDEBUG before including assert.h, or compile with -DNDEBUG. When NDEBUG is defined, assert(expression) expands to nothing and is removed from the binary. This is the standard way to strip assertions from production builds.
No. assert is for programmer mistakes and internal invariants during development. User-facing errors (bad files, invalid forms) need proper error handling—return codes, errno, or error messages—not abort(). Users should never crash your app because of assert.
The implementation writes a message like 'Assertion `expr' failed' with file and line to stderr, then calls abort(). The program terminates immediately. There is no recovery—design assert for impossible states you want to catch while debugging.
assert checks a runtime expression. static_assert (C11) checks a compile-time condition and fails the build if false. Use static_assert for type sizes and constants known at compile time; use assert for runtime assumptions.
Did you know?

The name NDEBUG means “no debug”—defining it tells the implementation to omit assertion checks. Many release build scripts pass -DNDEBUG automatically alongside optimizations, so never put essential logic inside an assert expression.

Explore C Standard Library Headers

Continue with ctype.h or browse the library index.

Standard Library Index →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful