Module 2: Pointers & Memory Model

The core of C — what most courses get wrong

Lessons

IDTitleDuration
2-1The Memory Model25 min
2-2Pointers: &, *, and Arithmetic35 min
2-3Arrays Are Pointers (Almost)30 min
2-4const, NULL, and Dangling Pointers25 min
2-5Double Pointers and void *30 min
2-6Function Pointers25 min

Project

| 2-proj | Project: Implement the String Library | 60 min |

Key Concepts

pointers, memory-layout, arrays, function-pointers, undefined-behaviour

Pedagogical Notes

The module teaches pointers through concrete address printing before introducing any abstraction. Lessons build in this order:

  1. Memory regions first (2-1) — the mental model of where things live. Stack grows down, heap grows up, text is read-only. Students print addresses and observe the layout pattern. This grounds everything that follows.

  2. Pointer syntax disambiguation (2-2) — the critical insight that * means three different things (int *p declaration, *p dereference, *(p+n) arithmetic). The swap example is the canonical demonstration of why C uses pointers for mutation rather than reference semantics.

  3. Array decay (2-3) — the single most misunderstood rule. Arrays are not pointers, but they decay to one in almost every context. The sizeof exception is the key diagnostic. String null-termination is the practical consequence.

  4. const and dangling (2-4) — two correctness tools. const on a pointer parameter is a contract: “I won’t modify what you gave me.” A dangling pointer is the consequence of breaking the ownership rule — the memory outlived its contract. AddressSanitizer is introduced here.

  5. Double pointers and void* (2-5) — the pattern for modifying a pointer through a function, and the type-erased void * that makes malloc/qsort work. The (ia > ib) - (ia < ib) comparator idiom avoids overflow.

  6. Function pointers (2-6) — functions are data at an address. Dispatch tables replace switch statements. typedef is mandatory for readability. This is C’s mechanism for what OOP languages do with interfaces.

Common Traps

  • sizeof after decay: sizeof(arr) is 20 inside the declaring scope; pass it to a function and it becomes 8 (pointer size). Students must pass length separately.
  • strncpy does not null-terminate when truncation occurs — use snprintf or the str_ncopy pattern from exercise 3.2 instead.
  • Writing to string literals: char *p = "hello"; p[0] = 'X'; is UB. String literals live in read-only text segment; always use const char *.
  • Comparator overflow: return a - b in a qsort comparator overflows for large negative integers; the correct idiom is (a > b) - (a < b).

Key Code Examples

/* The * disambiguation — three different syntactic roles */
int x = 42;
int *p = &x;   /* declaration: * is part of the type */
*p = 100;      /* expression:  * is dereference       */
int *q = p + 1;/* arithmetic:  p+1 skips sizeof(int) bytes */
/* Array decay — where sizeof differs */
int arr[5];
int *p = arr;            /* p == &arr[0] */
sizeof(arr);             /* 20 — no decay */
sizeof(p);               /* 8  — pointer size */
/* void * — the generic pointer; qsort comparator pattern */
int cmp_int(const void *a, const void *b) {
    int ia = *(const int *)a;
    int ib = *(const int *)b;
    return (ia > ib) - (ia < ib);   /* never use ia - ib: overflows */
}

Cross-Module Dependencies

  • Requires m01-c-is-not-java — compilation model, C types, integer arithmetic
  • Required by m03-dynamic-memory — malloc/free operate on void *; ownership rules build directly on dangling pointer concepts from lesson 2-4
  • Required by m04-structs-unions-bitfields — struct member access via -> is pointer dereference; offsetof uses pointer arithmetic
  • Referenced by m05-c-preprocessorcontainer_of macro uses offsetof and pointer arithmetic; introduced in ch03 Real-World Connection

PDF Status

  • PDF ch03 (Pointers and the Memory Model) — Done
    • Key Concepts: pointer syntax map table, arithmetic scaling, array decay, NULL, void *, double pointers, function pointer syntax
    • Going Deeper: pointer provenance & strict aliasing, const-correctness & propagation, void * and the type system, function pointers
    • Real-World: Linux kernel container_of — intrusive linked list pattern
    • Exercises: 3.1 Map the address space, 3.2 str_ncopy, 3.3 void * arithmetic, 3.4 const-correct linked list
    • Extended project: Implement the String Library (10 functions, ASan/UBSan clean)