Now I have everything. Here’s the complete master index.

C Pointers & Memory — Complete Master Index

Everything from the hardware up to the most advanced pointer techniques in C.

TIER 0 — The Hardware Foundation (What Memory Actually Is)

0.1   What RAM is — volatile storage, addressable bytes, physical addresses
0.2   The CPU and memory bus — how the CPU reads/writes RAM
0.3   Registers — the fastest storage, no address, CPU-local
0.4   The memory hierarchy — registers → L1 → L2 → L3 → RAM → disk
0.5   Cache lines — 64 bytes, what gets loaded on a read miss
0.6   Cache coherency — what happens when multiple cores read/write
0.7   Word size — 32-bit vs 64-bit, size of a pointer
0.8   Byte addressability — every byte has an address, not every bit
0.9   Endianness — little-endian vs big-endian, byte order in memory
0.10  Big-endian vs little-endian — which end of a multi-byte integer is stored first
0.11  x86-64 address space — 48-bit virtual, 64-bit pointer width
0.12  Physical address space — limited by RAM, managed by OS
0.13  Memory-mapped I/O — devices appear as memory addresses

TIER 1 — Virtual Memory (The OS Abstraction)

1.1   What virtual memory is — each process sees its own private address space
1.2   Why virtual memory exists — isolation, protection, overcommit
1.3   Virtual address vs physical address — the mapping the kernel manages
1.4   Pages — fixed-size chunks (4KB typical), unit of mapping
1.5   Page frames — physical memory divided into same-size frames
1.6   Page table — kernel data structure mapping virtual pages → physical frames
1.7   Multi-level page tables — 4-level on x86-64 (PGD→PUD→PMD→PTE)
1.8   Page table entries (PTE) — frame number + permission bits + present bit
1.9   MMU — Memory Management Unit, hardware that does address translation
1.10  TLB — Translation Lookaside Buffer, hardware cache of recent translations
1.11  TLB miss — page table walk, expensive, why locality matters
1.12  TLB flush — on context switch, why process switches are costly
1.13  Page fault — accessing unmapped/not-present page, kernel handles it
1.14  Minor page fault — page exists but not mapped yet (demand paging)
1.15  Major page fault — page must be fetched from disk (swap)
1.16  Segmentation fault — accessing page with wrong permissions or unmapped
1.17  SIGSEGV — signal delivered on segfault, usually kills process
1.18  Demand paging — pages only loaded when first accessed
1.19  Copy-on-write (COW) — fork shares pages until one writes
1.20  Memory overcommit — kernel promises more than physical RAM exists
1.21  OOM killer — kernel kills processes when physical RAM exhausted
1.22  Swap space — disk used to page out cold pages
1.23  Huge pages — 2MB/1GB pages, fewer TLB entries needed
1.24  ASLR — Address Space Layout Randomization, security feature
1.25  Canonical addresses — on x86-64, bits 48-63 must sign-extend bit 47

TIER 2 — Process Address Space Layout

2.1   The virtual address space — what a process "sees"
2.2   Address space diagram — low to high: text → data → BSS → heap ↑ ... ↓ stack
2.3   Text segment (.text) — compiled machine code, read-only, executable
2.4   Read-only data (.rodata) — string literals, const globals
2.5   Initialized data segment (.data) — global/static vars with non-zero init
2.6   BSS segment (.bss) — uninitialized global/static vars, zeroed by kernel
2.7   Why BSS exists — no disk space wasted for zeroed data
2.8   Heap — dynamic allocations, grows upward from end of BSS
2.9   Memory-mapped region — mmap allocations, shared libs, anonymous mappings
2.10  Stack — automatic storage, grows downward from high addresses
2.11  Kernel space — top of address space, not accessible from userspace
2.12  Reading /proc/self/maps — seeing your own process layout
2.13  /proc/self/smaps — detailed memory stats per region
2.14  Inspecting with pmap — visual address space map
2.15  The "gap" between heap and stack — virtual, not physical
2.16  Stack limit — RLIMIT_STACK, typically 8MB
2.17  Multiple threads — each thread has its own stack in the mmap region
2.18  Shared library layout — loaded into mmap region, shared across processes
2.19  Environment variables and argv — stored above stack at process start
2.20  VDSO / vsyscall — kernel-mapped code for fast syscalls (gettimeofday, etc.)

TIER 3 — The Stack in Depth

3.1   What the stack is — LIFO region for function call management
3.2   Stack pointer (RSP on x86-64) — register pointing to top of stack
3.3   Frame pointer (RBP on x86-64) — register pointing to base of current frame
3.4   Stack frame (activation record) — everything pushed for one function call
3.5   Stack frame contents — return address, saved registers, locals, params
3.6   Calling conventions — who saves which registers, how args are passed
3.7   x86-64 System V ABI — args in RDI, RSI, RDX, RCX, R8, R9, rest on stack
3.8   Return values — RAX for integer/pointer, XMM0 for float
3.9   CALL instruction — pushes return address, jumps to function
3.10  RET instruction — pops return address, jumps back to caller
3.11  PUSH / POP — how values are written/read from stack
3.12  Prologue / epilogue — setup and teardown of stack frame
3.13  Red zone — 128 bytes below RSP usable without adjusting RSP (x86-64)
3.14  Stack growth direction — downward on x86/ARM/most architectures
3.15  Local variable layout — compiler assigns slots in frame
3.16  Stack canaries — sentinel value to detect overflows (-fstack-protector)
3.17  Stack overflow — recursion too deep, local too large, segfault on guard page
3.18  Stack guard page — unmapped page to catch overflows
3.19  Alloca — allocates on stack (adjusts RSP), freed on function return
3.20  VLA — variable-length array, also on stack, same risks as alloca
3.21  Non-local returns — longjmp restores saved RSP/RBP
3.22  Stack unwinding — exception handling, debugger backtraces
3.23  DWARF — debug format encoding stack layout for backtraces
3.24  Reading a stack trace — frame numbers, return addresses, function names
3.25  Never return pointer to local variable — the canonical beginner bug
3.26  Function inlining — no stack frame created, compiler optimization
3.27  Tail call optimization — callee reuses caller's frame

TIER 4 — The Heap in Depth

4.1   What the heap is — region for dynamic allocations of arbitrary lifetime
4.2   brk() / sbrk() — syscalls to extend the data segment (old heap growth)
4.3   mmap() for large allocations — glibc uses mmap for >MMAP_THRESHOLD
4.4   The allocator — userspace library managing the heap (glibc malloc)
4.5   malloc chunk — header + data, chunk metadata stored before user pointer
4.6   Chunk header — size, flags (prev_inuse, is_mmapped, non_main_arena)
4.7   Free list — linked list of free chunks
4.8   Bins — categorized free lists (fast, small, large, unsorted, tcache)
4.9   Tcache — thread-local cache of freed chunks (glibc 2.26+)
4.10  Fastbins — small fixed-size free chunks, no coalescing
4.11  Smallbins — doubly-linked lists for small chunks
4.12  Largebins — sorted by size for large chunks
4.13  Unsorted bin — chunks go here first before being sorted
4.14  Coalescing — merging adjacent free chunks to reduce fragmentation
4.15  Top chunk (wilderness) — the chunk at the end of heap
4.16  malloc_trim() — returning unused heap memory to OS
4.17  malloc_stats() / mallinfo() — inspecting allocator state
4.18  malloc_usable_size() — actual usable bytes in an allocation
4.19  Heap fragmentation — many small frees leaving unusable gaps
4.20  Internal fragmentation — wasted space inside an allocation (padding)
4.21  External fragmentation — free space exists but not contiguous enough
4.22  Heap exhaustion — malloc returns NULL, must always check
4.23  Heap overflow — writing past end of allocation, corrupts metadata
4.24  Use-after-free — heap metadata/data corruption, security exploit class
4.25  Double free — corrupts free list, classic exploit primitive
4.26  Heap spray — attacker technique to position data predictably
4.27  MALLOC_CHECK_ / MALLOC_PERTURB_ — glibc debug env vars
4.28  Electric fence / DUMA — each allocation on its own page, detect overflows

TIER 5 — Pointer Fundamentals

5.1   What a pointer is — a variable whose value is a memory address
5.2   Pointer size — sizeof(void*) == 4 on 32-bit, 8 on 64-bit
5.3   Pointer declaration syntax — T *p (binds to name, not type)
5.4   Reading complex declarations — right-to-left rule
5.5   Address-of operator & — gives address of a variable
5.6   Dereference operator * — reads/writes value at address
5.7   The pointer/pointee distinction — pointer is the variable, pointee is what it points at
5.8   Initialization — always initialize pointers, uninitialized = wild pointer
5.9   NULL — the null pointer constant, 0 cast to pointer
5.10  NULL representation — guaranteed to compare unequal to any valid pointer
5.11  Null pointer deref — SIGSEGV, always check before dereferencing
5.12  Printing pointers — %p format, cast to void* first
5.13  Pointer assignment — p = q makes p point to what q points to
5.14  Pointer comparison — == != < > only valid within same array
5.15  Pointer to pointer — T **pp, what it means, why it's needed
5.16  Triple pointer — T ***ppp, usually a design smell
5.17  Returning a pointer from a function — heap only, never stack locals
5.18  The "swap two ints" example — canonical first pointer exercise
5.19  Simulating pass-by-reference — passing &x, modifying *x in function

TIER 6 — Pointer Arithmetic

6.1   Pointer arithmetic rules — p + n moves by n * sizeof(*p) bytes
6.2   Why it scales by sizeof — pointers know their pointee type
6.3   p++ — advances by sizeof(*p) bytes
6.4   p-- — moves back by sizeof(*p) bytes
6.5   Pointer difference — (p - q) gives count of elements, type ptrdiff_t
6.6   Valid arithmetic range — within array + one past end
6.7   One-past-end pointer — valid to form, invalid to dereference
6.8   Walking an array with pointer — while (p < end) *p++ = 0
6.9   Pointer arithmetic on void* — illegal (GCC extension allows it, avoid)
6.10  Pointer arithmetic on char* — always 1 byte per increment
6.11  Pointer arithmetic and struct layout — not valid across struct members
6.12  Subtraction to get offset — (char*)p - (char*)base
6.13  Comparing pointers to different objects — undefined behavior
6.14  Array indexing is pointer arithmetic — a[i] == *(a + i) == i[a]
6.15  Negative indices — valid if pointer points far enough into array
6.16  Two-pointer technique — classic algorithm pattern

TIER 7 — Pointer Declarations (All Forms)

7.1   int *p           — pointer to int
7.2   int **p          — pointer to pointer to int
7.3   int *p[10]       — array of 10 pointers to int
7.4   int (*p)[10]     — pointer to array of 10 ints
7.5   int *f()         — function returning pointer to int
7.6   int (*f)()       — pointer to function returning int
7.7   int (*f)(int,int)— pointer to function taking two ints, returning int
7.8   int *(*f)(int)   — pointer to function returning pointer to int
7.9   int (*p[5])(int) — array of 5 pointers to functions
7.10  const int *p     — pointer to const int (can't modify pointee)
7.11  int * const p    — const pointer to int (can't modify pointer)
7.12  const int * const p — const pointer to const int (neither modifiable)
7.13  int (* const p)(int) — const function pointer
7.14  void *p          — generic pointer, no arithmetic, no dereference
7.15  Using cdecl tool — parsing insane declarations automatically
7.16  typedef to simplify — typedef int (*Comparator)(const void*, const void*)

TIER 8 — const and Pointers

8.1   const T *p — read-only view of pointee, pointer itself is mutable
8.2   T * const p — pointer is fixed, pointee is mutable
8.3   const T * const p — both fixed
8.4   Passing const T* to functions — communicates read-only intent
8.5   Returning const T* — caller cannot modify through it
8.6   const-correctness — discipline of propagating const through all code
8.7   Casting away const — (T*)const_ptr — legal syntax, UB if you actually write
8.8   String literals and const — char *p = "hello" — should be const char*
8.9   const and arrays — const int arr[] means elements are const
8.10  Pointer to const vs const pointer — frequent source of confusion
8.11  Why const propagation matters — compiler optimizations rely on it

TIER 9 — void* and Generic Pointers

9.1   void* — pointer to unknown type
9.2   Implicit conversion — any pointer implicitly converts to/from void* in C
9.3   No arithmetic on void* — undefined (GCC extension allows, -Wpedantic warns)
9.4   No dereference of void* — must cast first
9.5   malloc returns void* — no cast needed in C (required in C++)
9.6   Using void* for generics — qsort, bsearch, callback context
9.7   The context/userdata pattern — void* carrying caller state into callbacks
9.8   Type punning via void* — casting through void* does NOT bypass aliasing
9.9   Function pointer vs void* — function pointers are not void* (different size possible)
9.10  NULL as void* — (void*)0

TIER 10 — Arrays and Pointers

10.1  Array decay — array name in expression becomes pointer to first element
10.2  What decays — array name in most expression contexts
10.3  What does NOT decay — sizeof(arr), &arr, string literal init of char[]
10.4  sizeof array vs sizeof pointer — the critical difference
10.5  &arr vs arr — &arr is T(*)[N], arr is T* — different types, same address
10.6  Arrays are NOT pointers — they are different types, just often compatible
10.7  Passing arrays to functions — always decays to pointer, size information lost
10.8  Passing size separately — canonical pattern: f(int *arr, size_t n)
10.9  2D arrays — T a[R][C] is contiguous, a[i][j] == *(*(a+i)+j)
10.10 Pointer to row of 2D array — int (*p)[C] = a
10.11 Array of pointers — int *p[R], each pointing to separate allocation
10.12 The difference in memory layout — contiguous vs scattered
10.13 Jagged arrays — array of pointers to arrays of different lengths
10.14 Multidimensional array decay — only first dimension decays
10.15 Returning array from function — impossible directly, use pointer or struct
10.16 Flexible array member — struct { int n; int data[]; } — C99
10.17 Zero-length arrays — GCC extension, use FAM instead
10.18 String as array of char — null-terminated, every string is a char*

TIER 11 — Function Pointers

11.1  What a function pointer is — address of a function's machine code
11.2  Declaring a function pointer — int (*fp)(int, int)
11.3  Assigning — fp = &add or fp = add (same thing in C)
11.4  Calling — (*fp)(1,2) or fp(1,2) (same thing in C)
11.5  typedef for clarity — typedef int (*BinOp)(int, int); BinOp fp = add
11.6  Passing as argument — callbacks, generic algorithms
11.7  Returning from function — factory pattern
11.8  Array of function pointers — dispatch table, state machine
11.9  Struct of function pointers — C vtable, simulating methods
11.10 NULL function pointer — check before calling
11.11 Function pointers vs void* — not guaranteed same size (POSIX requires it)
11.12 Casting function pointers — undefined behavior in general, POSIX exception
11.13 dlsym() — loading function pointer at runtime from shared library
11.14 Signal handlers as function pointers — void (*)(int)
11.15 qsort comparator — classic function pointer callback example
11.16 Computed goto (GCC) — labels as values, pointer to label, dispatch table

TIER 12 — Struct Pointers and Memory Layout

12.1  Pointer to struct — struct Foo *p, use -> to access members
12.2  -> operator — p->field is (*p).field exactly
12.3  Struct size — sum of member sizes + padding
12.4  Alignment requirement — each member must start at multiple of its alignment
12.5  Padding — compiler inserts unused bytes to satisfy alignment
12.6  Trailing padding — struct padded to multiple of its largest member's alignment
12.7  Example — struct {char a; int b} has 3 bytes padding after a
12.8  sizeof surprises — struct size ≠ sum of member sizes
12.9  offsetof(struct, member) — byte offset of a member
12.10 Manual padding control — reorder members to minimize padding
12.11 __attribute__((packed)) — eliminate padding (alignment penalty, UB risk)
12.12 alignas(N) — force member or variable to specific alignment (C11)
12.13 Pointer to member — &p->field gives address of that field
12.14 Pointer aliasing via struct — accessing struct through incompatible type = UB
12.15 container_of macro — given pointer to member, get pointer to containing struct
12.16 container_of implementation — (Type*)((char*)ptr - offsetof(Type, member))
12.17 Intrusive linked lists — embedding list node inside struct, used in Linux kernel
12.18 Forward declaration — struct Foo; lets you use Foo* before defining struct
12.19 Opaque pointer pattern — hide struct behind typedef in header, define in .c
12.20 Self-referential struct — struct Node { int val; struct Node *next; }
12.21 Pointer to union — accessing via wrong union member through pointer = UB
12.22 Bit field pointers — you cannot take address of a bit field

TIER 13 — Memory Alignment in Depth

13.1  What alignment is — objects must start at addresses divisible by their alignment
13.2  Natural alignment — int aligned to 4, double to 8, pointer to 8 (on 64-bit)
13.3  _Alignof(T) — alignment requirement of type T
13.4  _Alignas(N) — specify alignment of a variable or struct member
13.5  aligned_alloc(align, size) — heap allocation with guaranteed alignment (C11)
13.6  posix_memalign() — POSIX aligned allocation
13.7  Why alignment matters — misaligned access: slow (x86), bus error (ARM)
13.8  SIGBUS — signal for misaligned access on strict architectures
13.9  Packed structs and alignment — __attribute__((packed)) can cause misalignment
13.10 DMA and alignment — device memory often requires specific alignment
13.11 SIMD alignment — SSE requires 16-byte, AVX requires 32-byte alignment
13.12 Stack alignment — x86-64 ABI requires 16-byte alignment at CALL
13.13 Checking alignment at runtime — ((uintptr_t)p & (align-1)) == 0
13.14 max_align_t — type with maximum fundamental alignment
13.15 Alignment and casting — casting to stricter alignment = UB if not aligned
13.16 struct padding to ensure alignment — compiler adds trailing padding
13.17 Allocator alignment guarantee — malloc returns at least max_align_t aligned
13.18 Page alignment — mmap returns page-aligned (4096 bytes)

TIER 14 — Strict Aliasing

14.1  What aliasing is — two pointers referring to the same memory
14.2  The strict aliasing rule — an object shall only be accessed through its effective type
14.3  Effective type — the type of the object as it was last stored to
14.4  Why the rule exists — enables compiler to assume two different-type pointers don't alias
14.5  Optimization consequence — compiler may reorder, cache, or eliminate loads/stores
14.6  The classic violation — int i; float *fp = (float*)&i; *fp = 1.0f; *i = ... (UB)
14.7  What types may alias — exhaustive list from C standard §6.5p7:
        - compatible type
        - signed/unsigned version of same type
        - qualified version (const, volatile)
        - aggregate or union containing compatible type
        - char, unsigned char, signed char (always may alias anything)
14.8  char* exception — char* can alias any type, used in memcpy
14.9  unsigned char* — safe to inspect any object byte-by-byte
14.10 void* — does not bypass strict aliasing, must cast to actual type first
14.11 The -fstrict-aliasing flag — enabled by default at -O2 and above
14.12 -fno-strict-aliasing — disables the rule, used by Linux kernel
14.13 -Wstrict-aliasing — compiler warning for aliasing violations
14.14 Real-world breakage — code that worked at -O0 breaks at -O2
14.15 The optimizer's view — alias analysis pass in LLVM/GCC
14.16 Detecting violations — TySan (type sanitizer, experimental)

TIER 15 — Type Punning

15.1  What type punning is — treating the same bytes as a different type
15.2  Why you'd want it — float bits, network protocol parsing, hashing
15.3  The WRONG way — float f = 1.0; int i = *(int*)&f — strict aliasing violation
15.4  The RIGHT way (C99+) — union type punning
15.5  Union type punning — write one member, read another — defined in C, not C++
15.6  The RIGHT way (always safe) — memcpy into target type
15.7  memcpy for type punning — compiler optimizes away the copy (no overhead)
15.8  Example — inspecting float bits: memcpy(&u32, &f32, 4)
15.9  Why memcpy works — reads/writes as char*, which bypasses strict aliasing
15.10 __builtin_memcpy — GCC/Clang intrinsic, same semantics, often better codegen
15.11 Trap representations — not all bit patterns are valid for all types
15.12 Endianness matters in type punning — bytes may be in different order
15.13 Portability — union punning is C99 defined, not C++ defined
15.14 Compilers in practice — all major compilers accept union punning
15.15 -fno-strict-aliasing workaround — disables optimization, not recommended

TIER 16 — restrict

16.1  What restrict means — this pointer is the only way to access this memory in this scope
16.2  restrict is a promise from programmer to compiler
16.3  Consequence — compiler may optimize assuming no aliasing with restricted pointer
16.4  restrict in function parameters — memcpy(void * restrict dst, const void * restrict src, size_t n)
16.5  restrict on local variables — less common, still valid
16.6  What happens if you lie — undefined behavior, silent wrong results
16.7  memcpy vs memmove — memcpy has restrict, memmove does not (handles overlap)
16.8  FORTRAN comparison — restrict gives C FORTRAN-level aliasing guarantees
16.9  Auto-vectorization — restrict enables compiler to generate SIMD code
16.10 restrict and pointers to pointers — T * restrict *pp (rare)
16.11 Checking restrict correctness — no tool enforces it, manual discipline only
16.12 restrict is C99 — not in C89, not in C++ (but compilers support __restrict)

TIER 17 — volatile and Pointers

17.1  What volatile means — every access must go to memory, no caching in registers
17.2  When volatile is needed — hardware registers, signal handlers, setjmp
17.3  volatile T *p — pointer to volatile object
17.4  volatile and optimization — compiler must not reorder or eliminate accesses
17.5  volatile is NOT atomic — two threads accessing volatile = data race = UB
17.6  volatile is NOT a memory barrier — CPU can still reorder
17.7  volatile vs _Atomic — volatile for hardware/signals, _Atomic for threading
17.8  Memory-mapped I/O — volatile uint32_t *reg = (volatile uint32_t *)0xDEAD0000
17.9  Signal handlers — volatile sig_atomic_t flag = 0 — correct pattern
17.10 setjmp/longjmp interaction — locals must be volatile to survive longjmp
17.11 volatile in device drivers — every register access must actually happen
17.12 The Linux kernel's READ_ONCE/WRITE_ONCE — portable volatile access macros
17.13 Common mistake — using volatile for thread communication (use _Atomic)

TIER 18 — Pointer Provenance (Abstract Machine Model)

18.1  What provenance is — a pointer carries the identity of its source allocation
18.2  Why provenance exists — enables alias analysis and optimization
18.3  The problem — two pointers with same address but different provenance
18.4  Example — uintptr_t u = (uintptr_t)p; int *q = (int*)u; — q has no provenance
18.5  Pointer-to-integer-to-pointer — roundtrip may lose provenance (UB in strict model)
18.6  PNVI-ae-udi — proposed provenance model (C standards committee)
18.7  What compilers do today — gcc/clang allow most roundtrips in practice
18.8  Why it matters for systems code — OS kernels, allocators, JITs use int-ptr casts
18.9  __builtin_launder (C++) — hints about provenance, no C equivalent
18.10 The "effective type" connection — provenance determines what types are valid
18.11 Practical advice — avoid int-pointer casts unless you control optimization flags
18.12 LLVM's treatment — LLVM IR has provenance semantics in its pointer model
18.13 Ongoing standardization — C23 / C2y still debating the exact model

TIER 19 — Pointer Bugs in Depth

Dangling Pointers

19.1  Dangling pointer — pointer to memory that is no longer valid
19.2  Dangling to stack — returning pointer to local variable
19.3  Dangling to freed heap — use-after-free
19.4  Use-after-free — reading/writing freed memory, heap corruption or exploit
19.5  Dangling after realloc — old pointer invalid if realloc moves allocation
19.6  Detecting dangling — set to NULL after free, ASan, valgrind
19.7  NULL after free — does NOT prevent dangling if pointer was aliased

Wild Pointers

19.8  Wild pointer — uninitialized pointer, value is garbage
19.9  Deref of wild pointer — UB, segfault if lucky, silent corruption if not
19.10 Detection — -Wuninitialized, valgrind, MSan

Buffer Overflows

19.11 Stack buffer overflow — overwriting return address, saved RBP, canary
19.12 Stack smashing — classic exploit, overwrite return address with shellcode
19.13 Heap buffer overflow — overwriting chunk metadata, adjacent allocations
19.14 Off-by-one — writing one byte past end (off-by-one null write is often exploitable)
19.15 Integer overflow leading to underallocation — malloc(n * sizeof(T)) where n overflows
19.16 Format string vulnerability — printf(user_input) reads/writes stack
19.17 Bounds checking — no automatic, must be done manually every time

Double Free

19.18 Double free — calling free() twice on same pointer
19.19 Effect — corrupts allocator free list, heap metadata
19.20 Exploitation — double free is a powerful exploit primitive
19.21 Prevention — set pointer to NULL immediately after free

NULL Dereference

19.22 NULL deref — accessing address 0, always SIGSEGV
19.23 NULL page — on Linux, first page is unmapped (can be changed via vm.mmap_min_addr)
19.24 Kernel NULL deref — historically exploitable, now mitigated

Memory Leaks

19.25 Memory leak — allocation without corresponding free
19.26 Gradual leak — accumulates over time, eventually OOM
19.27 One-time leak — allocated once, freed on exit (acceptable in some contexts)
19.28 Detecting leaks — valgrind --leak-check=full, ASan leak sanitizer

TIER 20 — Memory Debugging Tools

20.1  valgrind memcheck — invalid reads, invalid writes, use-after-free, leaks
20.2  valgrind output — reading error reports, suppression files
20.3  ASan (AddressSanitizer) — compile-time, -fsanitize=address
20.4  ASan shadow memory — 1/8 of address space used to track allocations
20.5  ASan redzones — poisoned bytes around allocations detect overflows
20.6  MSan (MemorySanitizer) — uninitialized reads, -fsanitize=memory
20.7  UBSan (UndefinedBehaviorSanitizer) — catches many UB cases at runtime
20.8  TSan (ThreadSanitizer) — data race detection, -fsanitize=thread
20.9  Electric Fence — each allocation gets its own page, deref past end = segfault
20.10 DUMA — like Electric Fence, also catches underwrites
20.11 GDB — watchpoints on memory addresses, catching writes
20.12 GDB heap commands — info malloc, heap debug commands
20.13 /proc/<pid>/maps — see all memory regions of a process
20.14 /proc/<pid>/smaps — per-region memory stats (RSS, PSS, dirty pages)
20.15 pmap — command-line process memory map viewer
20.16 Massif (valgrind) — heap profiler, peak heap usage
20.17 heaptrack — fast heap profiler, flamegraph output
20.18 mtrace — glibc malloc tracing (MALLOC_TRACE env var)
20.19 MALLOC_CHECK_ — glibc env var to enable extra heap checks
20.20 clang static analyzer — finds bugs without running code
20.21 cppcheck — static analysis for C, pointer bugs
20.22 Sparse — semantic checker used by Linux kernel

TIER 21 — Dynamic Memory — The Full API

21.1  malloc(size) — allocate size bytes, uninitialized, returns NULL on failure
21.2  calloc(n, size) — allocate n*size bytes, zero-initialized, overflow-safe multiply
21.3  realloc(ptr, size) — resize allocation, may move, old pointer invalid after call
21.4  realloc return value — always use temporary: tmp = realloc(p, n); if(!tmp) ...
21.5  free(ptr) — release allocation, ptr must be from malloc/calloc/realloc
21.6  free(NULL) — always safe, no-op
21.7  aligned_alloc(align, size) — C11, alignment must be power of two, size multiple of align
21.8  posix_memalign(&ptr, align, size) — POSIX, align must be power of two ≥ sizeof(void*)
21.9  valloc(size) — page-aligned allocation (obsolete, avoid)
21.10 memalign(align, size) — like posix_memalign but older API
21.11 alloca(size) — stack allocation, freed on function return, no failure return
21.12 malloc_usable_size(ptr) — glibc extension, actual bytes in chunk
21.13 malloc_trim(pad) — return free memory to OS
21.14 mallopt() — tuning malloc parameters (M_MMAP_THRESHOLD, M_TRIM_THRESHOLD)
21.15 mallinfo() / mallinfo2() — heap statistics struct

TIER 22 — Custom Allocators

22.1  Why custom allocators — performance, locality, determinism, debugging
22.2  Arena allocator — large slab, bump pointer, free all at once
22.3  Arena implementation — char *base, *cur, *end; void* alloc(size_t n)
22.4  Arena advantages — O(1) alloc, zero fragmentation, O(1) free-all
22.5  Arena use cases — request-scoped data, parser, game frame
22.6  Pool allocator — fixed-size objects, free list, O(1) alloc/free
22.7  Pool implementation — intrusive free list, objects store next pointer when free
22.8  Pool advantages — no fragmentation for fixed size, cache-friendly
22.9  Pool use cases — connection objects, AST nodes, fixed-size messages
22.10 Stack allocator — push/pop allocations, lifo only
22.11 Slab allocator — kernel-style, cache-aware, object constructor/destructor
22.12 Buddy allocator — powers-of-two sizes, fast coalescing
22.13 Freelist allocator — general purpose, best-fit or first-fit
22.14 Memory tagging — tag pointers with allocation ID for debugging
22.15 Ownership tracking in allocator — knowing who allocated what
22.16 Thread-local allocator — per-thread slab to avoid locking
22.17 Scope-based allocation — C++ RAII style simulated with cleanup attribute
22.18 GCC cleanup attribute — __attribute__((cleanup(fn))) automatic free on scope exit
22.19 Obstack — GNU library for stack-discipline arena allocator

TIER 23 — Advanced Pointer Techniques

23.1  Tagged pointers — using low bits of aligned pointer to store flags
23.2  Why it works — aligned pointers have 0 in low bits (e.g. 3 bits free for 8-byte aligned)
23.3  Extracting tag — ptr & 0x7
23.4  Extracting address — ptr & ~0x7
23.5  Use cases — NaN-boxing (JavaScript engines), GC mark bits, lock-free algorithms
23.6  Fat pointers — pointer + length + metadata in a struct
23.7  XOR linked list — node stores prev XOR next, saves one pointer per node
23.8  Sentinel nodes — dummy head/tail nodes simplify linked list logic
23.9  Self-referential pointers — pointer within struct pointing back to itself or parent
23.10 Handle tables — integer keys into array of pointers (avoids pointer exposure)
23.11 Relative pointers — store offset from base instead of absolute address (relocatable)
23.12 Compressed pointers — storing 32-bit offset into known base (JVM compressed oops)
23.13 Pointer swizzling — converting file offsets to pointers on load (mmap'd databases)
23.14 Interior pointers — pointing into the middle of an allocation
23.15 Pointer reversal — Deutsch-Schorr-Waite graph traversal without recursion or stack
23.16 Lock-free pointer operations — CAS on pointers, ABA problem
23.17 ABA problem — pointer value cycles back, CAS falsely succeeds
23.18 Hazard pointers — safe memory reclamation in lock-free structures
23.19 RCU (Read-Copy-Update) — pointer publishing protocol (Linux kernel)
23.20 Memory-mapped pointer — pointer into mmap'd file, persists across runs
23.21 Capability pointers — hardware-enforced bounds checking (CHERI architecture)

TIER 24 — Pointer Safety Patterns & Idioms

24.1  Always initialize — T *p = NULL; never leave uninitialized
24.2  Always check malloc — if (!p) { handle error }
24.3  Null after free — free(p); p = NULL; (only helps if not aliased)
24.4  Single ownership — one owner per allocation, clear who frees
24.5  Ownership transfer — documented in function signature/comment
24.6  const for read-only access — communicate intent, enable optimization
24.7  Bounds always paired — T *buf, size_t buflen — always together
24.8  Never return pointer to local — return heap or static only
24.9  Don't hold pointer across realloc — realloc may invalidate
24.10 Opaque pointer pattern — hide internals, control lifetime
24.11 Cleanup goto pattern — single exit point ensures all pointers freed
24.12 Two-phase init — allocate, then initialize — separate concerns
24.13 Pointer validation — check alignment, check range, check against known bounds
24.14 Guard pages — mprotect PROT_NONE pages around sensitive allocations
24.15 Memory ownership documentation — comments: "caller must free", "owned by X"
24.16 Const-correct API — inputs as const T*, outputs as T*
24.17 Avoid aliasing in API — don't allow input and output to overlap unless documented
24.18 Compiler annotations — __attribute__((nonnull)), __attribute__((malloc))
24.19 Static analysis — clang-tidy, cppcheck, PVS-Studio pointer checks
24.20 Fuzzing pointer-heavy code — libFuzzer, AFL++ catch memory bugs

TIER 25 — Pointers in the C Memory Model (Concurrency)

25.1  Data race — two unsynchronized concurrent accesses where at least one is write
25.2  Data race on a pointer — UB even if "just reading the address"
25.3  Pointer publishing — making a new allocation visible to other threads
25.4  Release-acquire — writer does atomic_store(release), reader does atomic_load(acquire)
25.5  The message-passing pattern — publish pointer via atomic, guarantee pointee is ready
25.6  _Atomic(T*) — atomic pointer type
25.7  atomic_store / atomic_load — on pointer type
25.8  compare_exchange — atomic pointer CAS, foundation of lock-free structures
25.9  Hazard pointers — safe pointer deref in lock-free code
25.10 The read-side memory ordering problem — why relaxed is not enough for pointers
25.11 Volatile is not enough — volatile does not create synchronization
25.12 Signal handler and volatile — sig_atomic_t for flags, not for pointers
25.13 Memory barriers — __atomic_thread_fence, atomic_signal_fence
25.14 Thread-local storage — __thread, _Thread_local, each thread's own pointer
25.15 TSan — detecting pointer races at runtime

Quick Reference: What Lives Where

REGION       | WHO ALLOCATES | WHO FREES     | LIFETIME
-------|---------------|---------------|--------------------
Registers    | Compiler      | Compiler      | Within expression
Stack        | Compiler      | Compiler      | Until function returns
BSS/Data     | OS at load    | OS at exit    | Entire program
Heap         | You (malloc)  | You (free)    | Until you free it
mmap         | You (mmap)    | You (munmap)  | Until you unmap it
POINTER TYPE      | WHAT IT POINTS TO         | VALID AS LONG AS
------------|---------------------------|----------------------
Stack pointer     | Local variable            | Function is on stack
Heap pointer      | malloc'd allocation       | Not freed
Static pointer    | Global/static variable    | Always
String literal    | .rodata segment           | Always (read-only)
NULL              | Nothing                   | Never dereference
Dangling          | Freed/gone memory         | Never
Wild              | Garbage address           | Never

~280 topics across 25 tiers. This is the complete map from transistors to lock-free pointer algorithms.

Which tier do you start with?