How I Started Understanding C Pointers
The mental model that finally made pointers click, after weeks of only half-getting it — plus the exercises that forced the concept in.
For weeks, I could recite the definition of a pointer — "a variable that stores a memory address" — without actually feeling it. I could pass the exam-question version of the concept and still write buggy pointer code five minutes later. This post is the mental model that finally closed that gap, and the exercises that forced it in.
The model that worked: boxes and addresses
Stop thinking of a pointer as "a thing that points at other things." Think of it as two separate facts you have to track at once:
- The pointer variable itself lives at some address, and has a value.
- The value it holds is also an address — the address of something else.
Once I started drawing this on paper as two boxes connected by an arrow, instead of trying to hold it all in my head, the confusion mostly disappeared.
int x = 42;
int *p = &x;
Here, p is a box. Inside that box is the address of x. x is a different box, holding 42. *p means "go to the address stored in p, and give me what's there" — which is 42.
Where it actually broke for me: pointer to pointer
The exercise that exposed the gap in my understanding was a function meant to reallocate memory and have the caller see the new pointer. My first instinct was to pass a plain pointer:
void resize(int *arr, int newSize) {
arr = realloc(arr, newSize * sizeof(int)); // only changes the local copy
}
This compiles fine and appears to work inside the function — but the caller's original pointer never changes, because arr here is a copy of the address. Reassigning the copy doesn't touch the original variable back in main().
The fix is a pointer to a pointer:
void resize(int **arr, int newSize) {
*arr = realloc(*arr, newSize * sizeof(int));
}
// called as:
resize(&myArray, 20);
Now arr holds the address of the caller's pointer variable, so dereferencing once with *arr lets you actually reach in and change what the caller's pointer points to.
The exercise that made it stick
I didn't trust myself to actually understand this until I could:
- Write a function that swaps two integers using pointers.
- Write a function that resizes a dynamically allocated array in place (the example above).
- Implement a basic singly linked list — insert, delete, traverse — from scratch, no references, no copy-pasting.
Step three is the one I'd recommend if you're stuck where I was. A linked list forces you to constantly think in terms of "what does this box contain, and what does that value point to," across dozens of small operations, until it stops requiring conscious effort.
Why this matters for embedded work
I'm learning C specifically as groundwork for embedded systems, and pointers are not optional there — HAL (hardware abstraction layer) functions constantly return status through pointer output parameters, and you're directly manipulating memory-mapped registers, which are pointers, just to fixed hardware addresses. There's no getting around building real intuition here before moving further into STM32 work.
If you're in the same spot I was: stop reading more explanations and go draw the boxes yourself. It was the single highest-leverage thing I did.