Pointers and References, Explained
What a pointer actually is, how references differ, why they power linked lists and trees, and how higher-level languages hide raw addresses behind reference semantics.
Memory is just a long row of numbered boxes. A pointer is a box whose contents are the number of another box. Once you internalize that one sentence, half the mystery of C, linked lists, and “why did my object change?” disappears.
A Pointer Holds an Address
Every byte in a running program lives at a numeric address. A pointer is an ordinary variable whose value happens to be one of those addresses. When you have a pointer, you can dereference it — follow the address to read or write the value it refers to.
int x = 42;
int *p = &x; // p holds the address of x
printf("%d\n", *p); // dereference: prints 42
*p = 99; // write through the pointer
printf("%d\n", x); // x is now 99
The & operator takes the address of a variable; the * operator dereferences a pointer. Because p knows where x lives, writing through p changes x itself. That indirection is the whole point: two parts of a program can hold the same address and therefore see the same data. Pass a pointer into a function and the function can modify the caller’s variable, not a copy.
This is also why pointers enable dynamic data structures. A linked list is just nodes where each node holds a value plus a pointer to the next node; a binary tree node holds pointers to its children. The nodes can sit anywhere in memory — the pointers stitch them together. You can grow and shrink these structures at runtime by allocating new nodes and rewiring pointers, something a fixed-size array cannot do.
References Are Safer Aliases
A reference is a name that refers to an existing variable — an alias. In C++, once a reference is bound to a variable, using the reference is using that variable, with no * needed:
int x = 42;
int &r = x; // r is another name for x
r = 99; // x is now 99
A C++ reference is, under the hood, much like a pointer, but the language adds guardrails: it must be initialized when declared, it cannot be reseated to refer to something else, and it cannot (in well-formed code) be null. That makes references convenient for passing arguments without copying while avoiding the sharp edges of raw pointer arithmetic.
The word “reference” gets reused with different meanings across languages. In Java and Python, you never touch raw addresses at all. Variables that hold objects hold references to those objects, and the runtime manages the actual memory and garbage collection. Go sits in between: it has explicit pointers (*T, &x) but no pointer arithmetic, and the garbage collector keeps them safe. Python’s model is sometimes called “names bound to objects”: a name is a label attached to an object, and assignment rebinds the label rather than copying the object.
Null and the Billion-Dollar Mistake
A pointer can point at nothing — represented as null (NULL, nullptr, nil, or None depending on the language). Dereferencing a null pointer is a classic crash: a segmentation fault in C, a NullPointerException in Java, an AttributeError on None in Python.
Tony Hoare, who introduced the null reference in ALGOL W in 1965, later called it his “billion-dollar mistake,” because of the countless crashes and vulnerabilities it has caused over the decades. The lesson stuck. Modern languages fight back with non-nullable types, optionals, and the borrow checker: Rust has no null at all, encoding “maybe absent” in its Option type; Kotlin and Swift distinguish nullable from non-nullable types in the type system; Go’s nil and modern C++ smart pointers narrow the blast radius. The underlying idea is to make “this might be absent” something the compiler can check, rather than a landmine you discover at runtime.
FAQ
Understanding pointers is less about syntax than about the mental model: a value can either be the data or point to the data, and indirection is what lets independent pieces of code agree on the same object.
FAQ
Is a reference just a pointer with nicer syntax?
Why do linked lists and trees need pointers?
If Python hides pointers, why does mutating a passed list affect the caller?
Related reading
2026-06-22
TCP vs UDP, Explained Through What Breaks When You Pick Wrong
TCP and UDP aren't interchangeable. We walk through the exact failure modes — head-of-line blocking, silent packet loss, Nagle delays — that show up when you pick the wrong transport.
2026-06-22
Write-Ahead Logging: How Databases Survive a Power Cut
How write-ahead logging keeps your data intact when the machine dies mid-write — the log-first rule, fsync, checkpoints, and why PostgreSQL and SQLite both rely on it.
2026-06-22
Backpressure, Explained Through a Queue That Won't Fall Over
What backpressure actually is, why an unbounded queue is a memory leak in disguise, and the four strategies a producer can take when a consumer falls behind.
2026-06-22
What a Bloom Filter Actually Saves You (and When It Lies)
A bloom filter trades a small false-positive rate for big memory savings. Here is the math behind the trade, where it pays off, and the failure mode that bites people.
2026-06-22
Idempotency, Explained Through the Retry That Doesn't Double-Charge
A practical look at idempotency keys: why a retried payment request shouldn't charge a card twice, how the pattern works, and where it quietly breaks in production.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.