HomeAboutBlogsProjectsMoisture Meter
Teaching RolesWorkshopsTalks
Contact
Blog Series

Pointers: Thinking in Memory

Eleven posts that rebuild pointers from scratch, one question at a time.

Pointers are the part of C and C++ where most learners give up, most bugs live, and most seniors eventually prove their worth. This series takes them apart slowly. Each post is built around a single idea, derived from the problem it solves, and paired with interactive visualisations you can play with. By the end, every major pointer concept, from addresses to unique_ptr to undefined behaviour, feels like something you built yourself.

Complete · 11 parts
What this series is
A careful construction of pointers and memory from first principles. No skipped steps, no hand-wavy metaphors. Every concept gets the same treatment: motivate, introduce, make interactive, test. Covers both C and modern C++.
Who it's for
Programmers who've seen pointers and want to actually understand them. Students learning C for the first time. Engineers doing systems, embedded, or game work. If you can read basic C syntax, you're ready. You don't need prior memory-management experience.
The question this series answers
How do you reason about a piece of memory you don't own, in a language that won't catch you when you're wrong?
The eleven posts

Each part builds on the last. Read in order. Sidebar on each page tracks where you are. Every post ends with a quiz you can use to check yourself before moving on.

Act IWhat pointers are and why they exist
Part 1Motivation
Why Pointers?
Before anything else, the reason. Shared state, dynamic structures, the difference between passing a thing and passing its address. A hands-on argument for why indirection is the feature that unlocks the rest of C.
Why it matters: Pointers aren't syntax. They're a tool, and knowing what they solve is what makes every later concept feel inevitable rather than arbitrary.
Part 2Mechanics
Addresses, &, and *
The two operators and the mental model. & asks "where?" and * asks "what's there?". A mental picture of memory as numbered boxes, and what it means for one box to hold the number of another.
Why it matters: The vocabulary. Every later post leans on reading declarations and dereferences fluently, without stopping to translate.
Part 3Common pitfall
Arrays Are Not Pointers
They look the same, behave similarly, and break in different places. Array-to-pointer decay, the sizeof trap, and why a function parameter that "takes an array" is really taking a pointer.
Why it matters: The single most common C confusion, and the one that corrupts mental models silently for years. Naming the difference is half the fix.
Act IIMemory and the bugs that live in it
Part 4Foundation
Stack, Heap, and Lifetime
Where memory actually lives. The stack as a fast, disciplined LIFO; the heap as a pool you manage yourself. What "lifetime" means and why returning a pointer to a local variable is the bug you write exactly once.
Why it matters: Every pointer has a lifetime, and every bug in the next post is about mismatching that lifetime with the pointer's use.
Part 5Bug taxonomy
The Three Villains
Null, wild, and dangling pointers. Three distinct failure modes with three distinct diagnoses. How to spot each one in code, what produces it, and the first line of defence against each.
Why it matters: Most of the bugs you'll ever write involving pointers fit into one of these three boxes. Having names for them turns "mystery segfault" into a checklist.
Part 6Composition
Pointers and Functions
Pass-by-pointer versus pass-by-value, output parameters, and function pointers as first-class citizens. Why C callbacks look the way they do, and how a library lets you plug in your own logic without knowing what it'll be.
Why it matters: Functions-plus-pointers is where C becomes composable. It's also where void* starts earning its keep, setting up Act IV.
Act IIIContracts the compiler can enforce
Part 7Contracts
The const Puzzle
Three words, one star, two positions. const int*, int* const, and const int* const look similar and mean very different things. The right-to-left reading rule, top-level vs low-level const, and why const-correctness spreads.
Why it matters: The first place the type system lets you encode intent. Once read fluently, entire codebases become easier to navigate.
Part 8Compiler's paranoia
Aliasing
Two pointers, one address, one very nervous compiler. Strict aliasing, type punning with memcpy, and restrict as a trust contract. Why const and restrict solve different problems, and what the compiler is allowed to assume about each.
Why it matters: Aliasing is the invisible reason hot loops aren't as fast as they could be, and it's behind a whole class of optimisation-level-dependent bugs.
Part 9Type erasure
The Typeless Pointer
A pointer that has forgotten what it points at. How malloc, qsort, callback user data, and opaque library handles all rest on the same mechanism. And the hazards that come with switching off type-checking.
Why it matters: Every generic, composable C API is some variation on the void* trick. Understanding it makes library design in C stop feeling like magic.
Act IVModern C++ and where seniors earn their pay
Part 10Modern C++
Smart Pointers
Ownership encoded in the type system. RAII, move semantics, and the three-pointer family: unique_ptr, shared_ptr, weak_ptr. When to reach for each, and why one of them is the default.
Why it matters: Half the memory bugs in C++ codebases die when the type system starts tracking ownership for you. Smart pointers are how that happens.
Part 11The capstone
Undefined Behaviour
The finale. Why UB exists, why compilers are allowed to be so aggressive about it, and how every bug from earlier posts traces back to the same shape of contract violation. A tour of the sanitizer toolkit (ASan, UBSan, TSan) and the discipline that keeps a codebase honest.
Why it matters: UB is what separates programmers who ship systems code from those who just write it. This post is where all ten previous parts cash in.
What this series gave you

A complete, working picture of memory in C and C++, built from its own questions.

Starting from "a pointer is a variable that holds an address," eleven posts later you've covered everything a working C or C++ programmer actually uses: the mechanics, the bugs, the contracts, the modern-C++ tools for ownership, and the discipline for staying out of undefined behaviour. You've seen the ideas motivated, not announced. You've watched the pieces assemble.

The tools and habits from these posts are what the rest of a C or C++ career is built on. Every complicated system, every clever data structure, every optimised inner loop, is some combination of addresses, lifetime, ownership, aliasing, and the type-system contracts that hold it all together. Get those right and the rest follows.

The next step isn't more reading. It's writing. Build something with dynamic memory. Ship it. Run it under AddressSanitizer. Read a serious C or C++ codebase and argue with its ownership decisions. The knowledge in this series turns into fluency the moment you start stress-testing it against real code.

If you're new here
Start with Part 1 and read in order. Each post takes 20–30 minutes, including the quiz. The interactives reward pausing and playing. Every post ends with a seven-question self-test; five correct means you're ready for the next one.
Where to go after
For C, the canonical next reads are K&R (still unmatched for its terseness) and Chris Wellons's blog. For modern C++, Scott Meyers's Effective Modern C++ and the C++ Core Guidelines are where the foundations you've built here start paying dividends at scale.