pickuma.
Dev Knowledge

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.

5 min read

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?+
Often, yes, at the implementation level — a C++ reference is typically compiled as a pointer. The difference is in the rules: a reference must be initialized, cannot be reseated, and is not meant to be null, which removes whole classes of pointer bugs. The 'reference' in Java or Python is a higher-level concept the runtime manages for you.
Why do linked lists and trees need pointers?+
Their nodes are allocated separately and can live anywhere in memory. Each node stores pointers to its neighbors or children, which is what links the scattered nodes into one structure. This lets the structure grow and shrink at runtime by allocating nodes and rewiring pointers, unlike a fixed contiguous array.
If Python hides pointers, why does mutating a passed list affect the caller?+
Because the variable holds a reference to the list object, and passing it shares that reference rather than copying the list. Both names point at the same object, so an in-place mutation like append is visible everywhere. Rebinding the parameter to a new object, by contrast, only changes the local name.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.