HomeAbout BlogsProjects Moisture Meter Teaching Roles Workshops Talks Contact
Pointers: Thinking in Memory

Why Pointers?

Before syntax, before arithmetic, before the asterisk soup. The reason pointers exist in the first place, and why learning them quietly upgrades everything else you know about programming.

A sentence that does a lot of work

Almost every tutorial I've seen starts the same way. "A pointer is a variable that stores the address of another variable." You read it, you nod, you move on to syntax, and six weeks later you're staring at a segfault wondering what any of it was for.

That sentence is technically correct and almost useless. It describes what a pointer is without ever explaining what problem pointers solve. It's like defining a car as "a box with four wheels that holds people." Fine. But you haven't said anything about why humans invented them, what life was like before, or why somebody was willing to pay for one. You've skipped the motivation and gone straight to the diagram.

So before any syntax, any &, any *, I want to answer a more honest question. Why do pointers exist at all? What would we lose if they didn't? And why does a feature that seems to exist just to torment undergraduates turn out to be one of the most important ideas a programmer can hold in their head?

The point of pointers isn't to be clever. It's to be honest about how computers actually work.

The problem nobody tells you they're solving

Imagine you're writing a program that manages patient records at a hospital. A single record has a name, a date of birth, a medical history, a list of current medications, maybe some scan results. Let's say the whole thing is around 500 bytes. It doesn't matter what the exact number is. What matters is that it's big compared to a plain integer.

Now suppose a function needs to update one patient's medication list. In a world without pointers, passing the patient to that function means making a copy. The caller has the original record. The function gets its own 500 byte copy. Any change the function makes lives only inside its copy. When the function returns, the copy gets thrown away and the original is untouched.

So you work around it. The function returns the modified copy, and the caller overwrites its original. That works, sort of, until the record is 5000 bytes, or until you have a thousand of them, or until you want two different functions to share one record. Suddenly every function call is shuffling piles of bytes around, and you're constantly asking "wait, which copy is the real one?"

A pointer collapses all of that. Instead of sending the whole record to the function, you send its address. Eight bytes, regardless of how big the record is. The function reaches back into the original and modifies it directly. No copying, no returning bulky structures, no ambiguity about which copy is real. There is only ever one record, and anyone with its address can find it.

A pointer is a tiny arrow that says "the thing is over there, go work on it."
Watch what actually moves through the wire.
Record size: 500 B
Animation speed: 1.0x
Without pointers pass by copy
sending 500 B every call
With pointers pass the address
sending just 8 B every call
caller's record bytes being copied pointer (the address) function's frame

The top lane streams every byte of the record from caller to function, over and over. Drag the record size up and watch the stream get fatter and slower. The bottom lane sends a single tiny arrow holding an address, in the same time, regardless of record size. This is the whole motivation for pointers in one picture. Everything else is detail.


Pointers are how the machine actually sees the world

Here's something most intro courses bury under syntax. Every variable in your program lives somewhere in memory. When you write int x = 5, the CPU doesn't care that you called it x. It cares that there is some address in RAM, say 0x7ffe4a1b, and that the number 5 got stored there. The name x is a helpful fiction the compiler keeps up for your benefit.

A pointer is the moment that fiction drops away. When you write int* p = &x, you're saying "forget the label, give me the actual address. I want to work at the level the machine works at." That sounds dramatic but it's the honest description. Pointers aren't a weird extra feature bolted onto C. They're C admitting out loud what every program was already doing in secret.

Variable names are for humans. Addresses are for machines. Pointers are where the two meet.

This sounds philosophical until you realise what falls out of it. Once you see variables as addresses, whole categories of bugs stop being mysterious. Why does passing an array to a function not copy it? Because the name of the array decays into a pointer, and you're only ever sending the address. Why does modifying something inside a function sometimes affect the caller and sometimes not? Because one version was passed by address and the other was passed by value. You stop memorising rules and start deducing them.

See a variable as what it really is: a number at an address.
1int x;
2x = 42;
3int* p;
4p = &x; // p now holds x's address
5*p = 99; // write to the address p holds
x (the value) p (holds an address) what p points to

Step through the code. Watch p become a container for an address, not a number. In step 5, writing through p reaches across the grid and changes x, because the address p holds is the address of x. This is the whole magic trick. Everything else is bookkeeping.


Every data structure you will ever love is made of pointers

There's a moment in most DSA courses where the instructor introduces linked lists and treats them as just another topic on the syllabus. Trees come next week. Graphs the week after. They all get their own chapter, their own diagrams, their own problem sets.

What rarely gets said out loud is that these aren't separate topics. They're all the same idea. A linked list is nodes connected by pointers. A tree is nodes connected by pointers in a specific shape. A graph is nodes connected by pointers with no shape rules at all. A hash table's collision chain is, yes, a linked list, which means more pointers. Remove pointers from the world and almost the entire data structures zoo disappears. You're left with arrays, and that's it.

Every non-trivial data structure is a sculpture made of pointers. If you can see the pointers, you can see the structure.

This matters because it reframes what you're actually learning when you learn DSA. You're not memorising a catalogue of exotic containers. You're learning how to arrange pointers so that certain operations become cheap. A linked list is arranged so that inserting at the front is cheap. A balanced tree is arranged so that searching is cheap. A hash table is arranged so that both of those are cheap on average, at the cost of some overhead. The differences between these structures are differences in how their pointers are wired.

Consider a classic question. Why is inserting at the front of a linked list O(1) but inserting at the front of an array O(n)? The usual answer is something about shifting elements. True, but why does one shift and not the other? Because an array stores its elements next to each other in memory, so inserting at the front forces everything else to slide over. A linked list stores its elements wherever, connected by pointers, so inserting at the front is just "make a new node, point it at the current head, update the head pointer." Three assignments, done.

Pointers let you rearrange structure without moving data.

That sentence, once it clicks, explains half of data structures in one line.


Performance lives in the gaps between pointers

Here's where the story takes a turn that surprises people. You'd think pointers make everything faster, since they avoid copying. Mostly they do. But they also introduce a cost that's invisible in code and brutal in practice. When you follow a pointer, you're asking the CPU to fetch something from a different address. If that address isn't already in cache, the CPU stalls while it waits for RAM, and RAM is roughly 100 times slower than cache.

So the same feature that makes linked lists possible is also the feature that often makes them slower than arrays in practice, even for operations where Big-O says the linked list should win. An array of a million integers sits in one contiguous block. The CPU prefetches the next cache line while you work on the current one. Iterating is close to free. A linked list of a million integers is scattered across the heap. Every node->next is a dice roll that might or might not be in cache. The CPU waits. A lot.

Same operation. Two layouts. Watch the cache miss.
Contiguous array
Elements sit next to each other
Cache hits0
Cache misses0
Time0 ns
Linked list
Nodes scattered across the heap
Cache hits0
Cache misses0
Time0 ns

Same 16 elements, same traversal, wildly different cost. The array lets the CPU prefetch like a conveyor belt. The linked list scatters nodes across memory, so every hop rolls the dice on whether the next node is in cache. This is why "what is the Big-O" is only half the question. The other half is "what does this look like in memory?"

This is the real lesson. Pointers don't just let you build structures. They force you to think about where things live. And once you start thinking that way, a lot of senior-engineer wisdom stops feeling mystical. Why is a flat array of structs often faster than an array of pointers to structs? Locality. Why do game engines prefer data-oriented design over traditional OOP? Locality. Why is "array of structs vs struct of arrays" a real debate? Locality. None of these questions even make sense if pointers are a black box to you.


Pointers force you to think about ownership

Here's the deepest reason to learn pointers well, and it's the one almost nobody states out loud. Pointers force you to answer two questions that every real program has to face but most programmers never articulate.

Who owns this memory? And how long does it live?

In a garbage-collected language, the runtime answers both for you, usually fine, occasionally catastrophically. In C, you answer them yourself, every time. Every malloc is a promise about who will eventually free it. Every returned pointer implicitly says "this will still be valid when you read it." Every struct that holds a pointer inherits a responsibility.

If you get these wrong, you don't usually get a clean error. You get something worse. The program keeps running, corrupting things quietly, until it crashes in some completely unrelated place fifteen minutes later. Or it leaks memory slowly until it dies at 3am. Or it gets exploited, because most security vulnerabilities you've ever heard of, Heartbleed, Shellshock, countless browser zero-days, are pointer bugs at their core. Microsoft and Google have both independently reported that about 70% of their critical security bugs are memory safety issues.

Memory has a life cycle. Good programs respect it. Bad programs get exploited.

Pointers teach you to think about ownership as a first-class concern, and that thinking transfers everywhere. It's why C programmers tend to write more careful APIs even in Python. It's why Rust's borrow checker makes sense as a formalisation of rules C programmers learned the hard way. It's why modern C++ invented unique_ptr to encode "sole owner" and shared_ptr to encode "shared ownership". None of these ideas are arbitrary. They're answers to questions pointers make you ask.


What you're actually learning when you learn pointers

Put all this together and a pattern emerges. Learning pointers isn't really about learning a C feature. It's about picking up a set of mental habits that quietly upgrade everything else you do.

  • Identity vs equality. Two pointers can hold the same address, pointing at the same thing. Or they can hold different addresses with equal values. These are different questions. Every language has this distinction. Pointer people never confuse them.
  • Mutation through sharing. If two variables reference the same object, changing one changes the other. Beginners in Python are constantly surprised by this. Pointer people expect it.
  • Cost of indirection. Every pointer dereference is a potential cache miss. Pointer people think about data locality without being asked.
  • Null as a real state. Absence is something you have to check for, not assume away. Tony Hoare called null "the billion dollar mistake" because he invented it. Pointer people have respect for it.
  • Lifetime reasoning. Files, sockets, locks, database connections, they all have ownership and lifetime issues. The RAII pattern generalises. Once you've felt pointer pain, you see resource management everywhere.

These habits cost almost nothing to carry and improve your code in every language you'll ever write. That's the real motivation. Not "you need pointers to pass the interview." Not "you need pointers to write C." But "pointers are a worldview, and programmers who have it write noticeably different code."


Where this series is going

Over the next posts, we'll earn all of this piece by piece. We'll start with the absolute basics, what & and * really do, why the asterisk is overloaded, and why int* a, b; does not declare two pointers. We'll dig into memory, the stack vs the heap, and the three villains of pointer bugs: null, wild, and dangling. We'll figure out how functions use pointers to pass things back, how callbacks are just function pointers wearing a hat, and why const int* and int* const mean very different things.

Later, we'll get to the interesting stuff. Opaque pointers and the PIMPL idiom. Strict aliasing and why the compiler is allowed to assume more than you think. Smart pointers in modern C++, and why unique_ptr is one of the most quietly brilliant ideas in the language. And finally, the sharp edges. Undefined behaviour. Alignment. The little places where pointer-literate engineers earn their salary.

The goal isn't to survive pointers. It's to start seeing the hidden structure they make visible.

For now, just sit with the motivation. Pointers aren't a torture device. They're a window. Every time you dereference one, you're peeking at how the machine actually moves data around, how structures compose, how ownership flows. Everything else in this series is just learning to look through that window without flinching.


Comments