C Standard Library: , , & Error Handling Masterclass

โšก C (C17 / C23 Standard) ๐ŸŸข Lesson 46 ๐Ÿ“‚ Phase 17: Standard Library Deep-Dive ๐Ÿ“… 2026 Comprehensive Master Edition
๐Ÿ“Œ Covered in this in-depth guide: assert() Defensive Assertions ยท NDEBUG Release Build ยท ptrdiff_t & offsetof ยท INT_MAX/CHAR_MIN ยท setjmp/longjmp Non-Local Jumps ยท Static Assertions _Static_assert

Welcome to Phase 17 (Chapter 46): C Standard Library โ€” <assert.h>, <stddef.h>, <limits.h> & Error Handling Masterclass! This chapter covers the defensive programming infrastructure of C: runtime assertions that catch impossible states, compile-time assertions for type sizes, platform limit constants, and non-local jumps for error recovery without exceptions.

1assert() โ€” Runtime Defensive Assertions

assert(expression) from <assert.h> evaluates an expression at runtime. If it evaluates to zero (false), the program prints an error message showing file, line number, and expression text, then calls abort().

When to use assert() vs if/return:

โ€ข assert() โ€” For detecting programmer bugs (impossible states, violated preconditions). Should NEVER fire in production.

โ€ข if/return error โ€” For handling runtime errors from external inputs (bad user data, I/O failures, NULL returns). Must be handled gracefully.

โ€ข Define NDEBUG in release builds (gcc -DNDEBUG) to strip ALL assert() calls to zero overhead.

C11 Static Assertions: _Static_assert(sizeof(int) == 4, "int must be 32 bits"); โ€” evaluated at compile time, zero runtime cost. Stops compilation if violated.

2stddef.h โ€” Core Type Definitions
IdentifierType / ValuePurpose
NULLNull pointer constantPlatform-correct null pointer (0 or (void*)0)
size_tUnsigned integerResult of sizeof. Array indices. Byte counts.
ptrdiff_tSigned integerResult of pointer subtraction. Can be negative.
offsetof(type, member)size_t macroByte offset of struct member from struct start. Used in binary protocols and container_of patterns.
3limits.h โ€” Platform Integer Boundaries

Every C platform has different widths for primitive types. <limits.h> exports the exact min/max constants for the current compilation target:

Key limits.h Constants:

โ€ข CHAR_BIT = 8 (bits per byte, always 8 on modern hardware)

โ€ข INT_MIN = -2,147,483,648 | INT_MAX = 2,147,483,647

โ€ข LONG_MAX = 9,223,372,036,854,775,807 (on 64-bit Linux)

โ€ข CHAR_MIN = -128 | CHAR_MAX = 127 (signed char)

โ€ข UINT_MAX = 4,294,967,295 (unsigned int)

4Complete Assertions & offsetof Demo
C โ€” assert, _Static_assert & offsetofโ–ถ Try in Compiler
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#include <limits.h>
#include <stdint.h>

/* Compile-time assertion: struct must be exactly 16 bytes for binary protocol */
typedef struct {
    uint8_t  type;    /* offset 0 */
    uint8_t  flags;   /* offset 1 */
    uint16_t length;  /* offset 2 */
    uint32_t id;      /* offset 4 */
    uint64_t payload; /* offset 8 */
} __attribute__((packed)) PacketHeader;

_Static_assert(sizeof(PacketHeader) == 16, "PacketHeader must be 16 bytes!");

static int divide(int a, int b) {
    assert(b != 0 && "Divisor must not be zero!");
    return a / b;
}

int main(void) {
    printf("INT_MAX  = %d\n", INT_MAX);
    printf("LONG_MAX = %ld\n", LONG_MAX);

    printf("offsetof(PacketHeader, id)      = %zu\n", offsetof(PacketHeader, id));
    printf("offsetof(PacketHeader, payload) = %zu\n", offsetof(PacketHeader, payload));

    printf("divide(10, 2) = %d\n", divide(10, 2));
    /* divide(10, 0) would trigger assert and abort */
    return 0;
}
5Technical FAQs

Q1: Should assert() be used for NULL pointer checks?

Only for NULL pointers that represent programmer bugs (e.g. internal preconditions). For NULL from user input or library calls, use if (!ptr) { handle_error(); }.

Q2: What happens when _Static_assert fails?

The compiler emits an error with your custom message string and stops compilation. Zero runtime overhead โ€” it is purely a compile-time gate.

Q3: What is the container_of macro?

A common Linux kernel macro using offsetof() to recover a pointer to the containing struct from a pointer to one of its members โ€” foundational to linked list implementations.

Q4: When would ptrdiff_t be negative?

ptrdiff_t = ptr2 - ptr1 is negative when ptr2 points to an earlier memory address than ptr1. This occurs when iterating backward through an array.

Q5: What is setjmp/longjmp used for?

setjmp() saves the CPU register state; longjmp() restores it โ€” effectively a non-local goto. Used in error recovery and parser frameworks. Extremely error-prone; avoid in new code.