C fundamentals ยท lesson 03

Pointers and addresses

A pointer is not magic. It's an integer. That integer happens to be an address in memory. Once you believe that, the rest follows.

in progress
12 min interactive

The confusion

Pointers scare people because the syntax is dense: int *p = &x has three different operators before you even get to a semicolon. And then there's **p and p->field and passing &p to a function.

But strip away the syntax and there's one idea: a pointer is a variable that holds an address. That's it. An address is just a number. The number tells the CPU where in RAM to look.

๐Ÿ’ก
Mental model: RAM is a warehouse. Every shelf has a number (its address). A pointer is a sticky note with a shelf number written on it. Dereferencing the pointer means walking to that shelf and looking at what's there.

The address-of operator: &

Every variable lives at some address in memory. The & operator gives you that address.

c
int x = 42; printf("%p\n", (void*)&x); // prints something like 0x7ffd3c8a4 // x lives at that address. &x gives you the address as a value. // The address itself is just an integer. On 64-bit: 8 bytes.

Declaring a pointer

A pointer variable holds an address. The type tells the compiler how to interpret the bytes at that address when you dereference.

c
int x = 42; int *p = &x; // p holds the address of x // *p means "the int at the address stored in p" printf("%d\n", *p); // prints 42 โ€” dereferences p *p = 100; // writes 100 to the address p points to printf("%d\n", x); // prints 100 โ€” x was changed via the pointer

What the * means โ€” it depends on context

The asterisk has two different meanings depending on where it appears, and this trips everyone up:

c
int *p = &x; // * in a declaration = "p is a pointer to int" int v = *p; // * as an operator = "value at the address in p"
โš ๏ธ
Read declarations right to left. int *p means "p is a pointer to int." int **pp means "pp is a pointer to a pointer to int." The type is not int* โ€” it's int and the * attaches to the variable. Writing int* p, q; declares only p as a pointer; q is an int.

Passing a pointer to a function

C passes everything by value. To modify a variable from inside a function, you pass its address. The function receives the address and writes through it.

c
void double_it(int *n) { *n = *n * 2; // write to the address } int x = 5; double_it(&x); // pass the address of x printf("%d\n", x); // prints 10

The NULL pointer

NULL is the zero address โ€” address 0. No valid object lives at address 0. It's used as a sentinel to mean "this pointer doesn't point to anything." Dereferencing NULL crashes your program with a segmentation fault. Always check before dereferencing a pointer you didn't initialize yourself.

๐Ÿšซ
An uninitialized pointer contains garbage. int *p; with no assignment โ€” p contains whatever bytes happened to be on the stack. Dereferencing it reads from a random address. Always initialize pointers: either to NULL or to a real address.

See it in memory

The visualization below shows a pointer variable and the variable it points to, side by side in memory. Watch the address stored in the pointer and the value at that address.