C assert.h Library Reference
Complete reference for assert.h - runtime assertions, NDEBUG, static_assert (C11), and debugging best practices.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Use assert() for debugging
- ✓Disable assertions with NDEBUG
- ✓Use static_assert for compile-time checks
- ✓Know when to assert vs error handling
?Why Use assert()?
Bugs are easier to find when they crash immediately rather than causing silent corruption.assert() catches impossible conditions during development!
Find Bugs Early
Crash at the source
Document Assumptions
Code is self-documenting
Zero Cost
Disabled in release
C11 Addition: static_assert checks conditions at compile time, catching errors before you even run the program!
01Introduction to assert.h
What is assert.h?
<assert.h> provides the assert() macro for runtime debugging. Assertions help catch programming errors by verifying that conditions you expect to be true actually are.
If an assertion fails, the program prints an error message and terminates.
Contents
| Name | Type | Description |
|---|---|---|
| assert(expr) | Macro | Runtime assertion check |
| NDEBUG | Macro | Define to disable assertions |
| static_assert | Keyword (C11) | Compile-time assertion |
02The assert() Macro
void assert(scalar expression);
If the expression evaluates to false (zero),assert() prints diagnostic information and calls abort().
1#include <stdio.h>2#include <assert.h>34int divide(int a, int b) {5 // Assert that divisor is not zero6 assert(b != 0); // Will abort if b is 07 return a / b;8}910int main() {11 int x = 10, y = 2;12 13 printf("Testing assertions...\n");14 15 // This assertion passes16 assert(x > 0);17 printf("x is positive: OK\n");18 19 // This assertion also passes20 int result = divide(x, y);21 printf("10 / 2 = %d\n", result);22 23 // This will FAIL and abort the program!24 printf("Now trying divide by zero...\n");25 result = divide(x, 0); // assert(b != 0) fails!26 27 // This line never executes28 printf("This won't print\n");29 30 return 0;31}3233/* Output:34Testing assertions...35x is positive: OK3610 / 2 = 537Now trying divide by zero...38example.c:6: divide: Assertion `b != 0' failed.39Aborted (core dumped)40*/Assertion Output Format:
When an assertion fails, it typically prints:filename:line: function: Assertion `expression' failed.
03Common Use Cases
1#include <stdio.h>2#include <assert.h>3#include <stdlib.h>4#include <string.h>56// 1. Validate function preconditions7void process_array(int *arr, int size) {8 assert(arr != NULL); // Array must exist9 assert(size > 0); // Size must be positive10 11 for (int i = 0; i < size; i++) {12 printf("%d ", arr[i]);13 }14 printf("\n");15}1617// 2. Check postconditions18int factorial(int n) {19 assert(n >= 0); // Precondition20 21 int result = 1;22 for (int i = 2; i <= n; i++) {23 result *= i;24 }25 26 assert(result >= 1); // Postcondition: factorial is always >= 127 return result;28}2930// 3. Validate invariants31struct Stack {32 int *data;33 int top;34 int capacity;35};3637void push(struct Stack *s, int value) {38 assert(s != NULL);39 assert(s->top < s->capacity); // Not full40 41 s->data[++s->top] = value;42 43 // Invariant: top is always valid index or -1 (empty)44 assert(s->top >= 0 && s->top < s->capacity);45}4647// 4. Verify memory allocation48void demo_allocation() {49 int *ptr = malloc(sizeof(int) * 100);50 assert(ptr != NULL); // Memory must be allocated51 52 // Use ptr...53 ptr[0] = 42;54 55 free(ptr);56}5758// 5. Check switch default (should never happen)59void handle_state(int state) {60 switch (state) {61 case 0: printf("State: INIT\n"); break;62 case 1: printf("State: RUNNING\n"); break;63 case 2: printf("State: STOPPED\n"); break;64 default:65 assert(0 && "Invalid state!"); // Should never reach66 }67}6869int main() {70 // Test 1: Array processing71 int arr[] = {1, 2, 3, 4, 5};72 process_array(arr, 5);73 74 // Test 2: Factorial75 printf("5! = %d\n", factorial(5));76 77 // Test 3: State handling78 handle_state(1);79 80 // Test 4: Memory81 demo_allocation();82 printf("All tests passed!\n");83 84 return 0;85}8687/* Output:881 2 3 4 5895! = 12090State: RUNNING91All tests passed!92*/04Disabling Assertions with NDEBUG
In release builds, you can disable all assertions by defining NDEBUG before including <assert.h>.
1// Method 1: Define NDEBUG in code (before including assert.h)2#define NDEBUG3#include <assert.h>45// Method 2: Define via compiler flag6// gcc -DNDEBUG program.c -o program78#include <stdio.h>910int main() {11 int x = -5;12 13 // With NDEBUG defined, this does NOTHING14 assert(x > 0); // Completely removed by preprocessor15 16 printf("Program continues even though x = %d\n", x);17 18 return 0;19}2021/* Output (with NDEBUG):22Program continues even though x = -523*/2425/* Without NDEBUG, would output:26example.c:10: main: Assertion `x > 0' failed.27Aborted28*/Important Warning:
When NDEBUG is defined, assert expressions are completely removed. Never put code with side effects inside assert():
// BAD: Side effect in assertionassert(++counter > 0); // counter won't increment with NDEBUG!// GOOD: Separate the side effect++counter;assert(counter > 0);05static_assert (C11)
static_assert(constant-expression, string-literal);
_Static_assert(constant-expression, string-literal); // Alternative
static_assert performs assertions at compile time. If the condition is false, compilation fails with the specified message.
1#include <stdio.h>2#include <assert.h>3#include <limits.h>45// Compile-time assertions6static_assert(sizeof(int) == 4, "int must be 4 bytes");7static_assert(sizeof(long) >= 4, "long must be at least 4 bytes");8static_assert(CHAR_BIT == 8, "char must be 8 bits");910// Verify struct size (useful for binary protocols)11struct NetworkPacket {12 unsigned short type;13 unsigned short length;14 unsigned int checksum;15};16static_assert(sizeof(struct NetworkPacket) == 8, 17 "NetworkPacket must be exactly 8 bytes");1819// Verify array size20#define BUFFER_SIZE 102421static_assert(BUFFER_SIZE >= 512 && BUFFER_SIZE <= 4096,22 "BUFFER_SIZE must be between 512 and 4096");2324// Verify enum values25enum Color { RED = 1, GREEN = 2, BLUE = 4 };26static_assert(RED | GREEN | BLUE == 7, "Color values must be distinct bits");2728int main() {29 printf("All compile-time assertions passed!\n");30 printf("sizeof(int) = %zu\n", sizeof(int));31 printf("sizeof(long) = %zu\n", sizeof(long));32 printf("sizeof(NetworkPacket) = %zu\n", sizeof(struct NetworkPacket));33 34 return 0;35}3637/* Output:38All compile-time assertions passed!39sizeof(int) = 440sizeof(long) = 841sizeof(NetworkPacket) = 842*/4344/* If assertion fails, compile error:45error: static assertion failed: "int must be 4 bytes"46*/C23 Update:
In C23, the message string is optional: static_assert(sizeof(int) == 4);
06Best Practices
✓ Do This
- Use for checking programming errors, not user input
- Add descriptive messages with
assert(cond && "message") - Check preconditions at function entry
- Use static_assert for compile-time requirements
- Keep assertions in debug builds
✗ Avoid This
- Don't use for validating user/external input
- Don't put side effects in assertions
- Don't use assert for recoverable errors
- Don't rely on assert in production code
Assert vs Error Handling
1#include <stdio.h>2#include <assert.h>3#include <errno.h>45// BAD: Using assert for user input6int bad_divide(int a, int b) {7 assert(b != 0); // Don't do this for user input!8 return a / b;9}1011// GOOD: Proper error handling for user input12int good_divide(int a, int b, int *result) {13 if (b == 0) {14 errno = EINVAL;15 return -1; // Return error code16 }17 *result = a / b;18 return 0; // Success19}2021// GOOD: Assert for internal invariants22void internal_function(int *ptr) {23 // This should never be NULL (programming error if it is)24 assert(ptr != NULL && "internal_function: ptr must not be NULL");25 26 *ptr = 42;27}!Code Pitfalls: Common Mistakes & What to Watch For
Copied code often suggests using assert() for input validation and error handling, which is fundamentally incorrect and dangerous in production code.
If you search online to "validate that a user-provided number is positive," it might suggest assert(n > 0);. This is wrong because assertions are disabled in release builds with NDEBUG, meaning your validation disappears in production!
The Trap: Online sources often conflate "checking a condition" with "asserting an invariant." They might also put side effects inside assert expressions (like assert(++count > 0)), which breaks completely when NDEBUG is defined because the entire expression is removed by the preprocessor.
The Reality: assert() is for catching programmer errors during development, not for validating user input or handling runtime errors. Use proper if-statements and error codes for runtime validation. Misusing assert leads to programs that work in debug mode but fail silently in production.
08Frequently Asked Questions
Q:What happens when an assert fails?
A: The program immediately aborts and prints an error message showing the file name, line number, and the failed condition. This helps developers quickly locate the bug during development.
Q:Why shouldn't I use assert for user input validation?
A: Because asserts are removed in release builds (when NDEBUG is defined). Invalid user input is expected at runtime and needs proper error handling that works in production, not just during debugging.
Q:What's the difference between assert and static_assert?
A: assert() checks conditions at runtime and aborts if false. static_assert checks at compile time — the code won't even compile if the condition is false. Use static_assert for type sizes, alignments, and other compile-time facts.
Q:How do I add a message to an assert?
A: Use the string trick:assert(condition && "error message"). Since a string literal is always true, the && only affects the message shown when the assert fails.
08Quick Reference
Runtime
assert(condition);assert(ptr != NULL);assert(x > 0 && "x must be positive");Compile-time (C11)
static_assert(sizeof(int) == 4, "msg");_Static_assert(cond, "message");13When to Use Assertions
Good Uses
- • Checking function preconditions
- • Validating internal invariants
- • Documenting assumptions in code
- • Catching programmer errors during development
Bad Uses
- • Validating user input
- • Checking file operations succeeded
- • Any runtime error that could happen in production
- • Expressions with side effects
errno.h Library
Next →stdint.h Library
14Debug vs Release Builds
Assertions in Debug Mode
During development, compile without NDEBUG to keep assertions active. They catch bugs early and provide clear error messages with file and line information.
Release Builds
Compile with -DNDEBUG for release builds. This removes all assert checks for maximum performance. Make sure your code handles errors properly without relying on assertions.
Test Your Knowledge
Related Tutorials
C errno.h Library Reference
Complete reference for errno.h - error handling with errno, perror(), strerror(), and common error codes.
C stdint.h Library Reference
Complete reference for stdint.h - fixed-width integers (int8_t, int32_t), limit macros, and portable integer code.
C stddef.h Library Reference
Complete reference for stddef.h - NULL, size_t, ptrdiff_t, offsetof, and fundamental type definitions.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!