roadmap
week 3 · day 16

Pointers, Truly

A pointer is an address that knows a type

Medium 36 min 180 xp
After this you can
  • Define a pointer precisely: a value that is a memory address, carrying a pointee type
  • Explain & (address-of) and * (dereference) in terms of the machine you already know
  • Predict what reading/writing through a pointer does to memory
  • See that a pointer dereference is exactly the [reg] addressing mode from day 6

The thing people fear, that you already understand

Pointers scare beginners because they're taught as syntax. You're not a beginner here — you know memory is a byte-addressed array (day 4), and you know [rbx] means "the value at the address in rbx" (day 6). A pointer is just that address, given a name and a type in C. There is nothing new to fear; there's just C syntax for machine ideas you already hold.

Key idea
A pointer is a variable whose value is a memory address. It's 8 bytes (day 15) holding the location of something else. Its *type* (int*, char*) records what kind of thing lives at that address — how many bytes to read and how to interpret them. Address + pointee-type: that's the whole definition.

Two operators, both familiar

  • `&x` (address-of) gives the address where x lives — the number of the memory cell. In machine
  • terms, this is lea (load effective address): compute where something is, don't read it.
  • *p** (dereference) means "go to the address in p and use the value there." In machine terms
  • this is *exactly* [p] — the bracket addressing mode. Reading *p is mov reg, [p]; writing
  • *p = v is mov [p], v.

So * is the C spelling of the brackets you learned in day 6. Dereference = follow the address.

ptr.cc
1int x = 42;
2int *p = &x; // p holds the address of x
3int y = *p; // y = the value at that address = 42 (a read: mov y, [p])
4*p = 99; // store 99 at x's address (a write: mov [p], 99)
5// now x == 99, because p pointed at x
p points at x
   name   address     bytes (value)
   x    0x7ffe1c   ->  42        <-- *p reads/writes HERE
   p    0x7ffe10   ->  0x7ffe1c  (p's value IS x's address)

   &x  = 0x7ffe1c   (the address of x)
   *p  = value at 0x7ffe1c = 42
   *p = 99  writes 99 into the bytes at 0x7ffe1c, so x becomes 99

Read the diagram: p is an ordinary 8-byte variable, but the *value it holds* is the address of x. *p follows that address to reach x's bytes. Assigning *p = 99 writes through the pointer and changes x — because they're the same memory. This is how a function can modify its caller's variable: pass &x, and the function writes through the pointer (the ABI passes that address in a register — day 13).

The type matters because of width

Why does a pointer carry a type at all, if it's just an address? Because the type says how many bytes to read/write at that address, and (next lesson) how far +1 moves. *p on an int* reads 4 bytes; on a char* reads 1; on a long* reads 8 — the same day-6 sizing, chosen by the pointer's type. An address with no type would be ambiguous about how much to touch. That's what a raw void* is — an address with the type deliberately erased, which is why you can't dereference one until you cast it back to a real type.

Predict first
int a = 5; int b = 6; int *p = &a; *p = *p + b; — after this runs, what are a, b, and does p change? Then: if we do p = &b; *p = 0;, what is b?
Note
The null pointer (NULL, address 0) means "points at nothing." Dereferencing it (*p when p is NULL) reads address 0, which the OS leaves unmapped on purpose, so you get a segfault instead of silent corruption. A crash on null-dereference is the OS catching a bug for you — later you'll learn that a *non*-null bad pointer is far more dangerous, because it might hit real memory.
finished reading?
Your task, you write the code

See a pointer change memory

In pointers.c: declare an int x, a pointer p = &x, and demonstrate (a) reading x through *p, (b) writing x through *p and confirming x changed, and (c) a function set_to(int *dst, int v) that writes v into the caller's variable via the pointer — proving pass-by-address. Print &x and p to show they're equal, and print addresses with %p. Then compile at -O0 and find the `lea` (for &x) and the `mov reg,[reg]` / `mov [reg],reg` (for reads/writes through p) in the disassembly. Uses only this lesson plus day 6/13.

deliverable: pointers.c (+ the disassembly lines for & and *)
build & run
$ gcc -O0 -g -Wall pointers.c -o pointers && ./pointers
$ objdump -d -M intel pointers
self-review before running
  • You showed p == &x by printing both
  • Writing *p changed x; your set_to() modified the caller's variable
  • You found lea for address-of and [reg] for dereference in the disassembly
  • You can explain the difference between `p = ...` and `*p = ...`
stretchMake a pointer to a pointer (int **pp = &p) and reach x with **pp. Draw the two hops. Then deliberately dereference a NULL pointer, run it, and see the segfault — the OS catching your bug.

Self-check

01What is a pointer?
02In machine terms, `*p` (dereference) is:
03`int *p = &a; *p = 7;` — what changed?
04Why does a pointer carry a type (int* vs char*) if it's just an address?
0/4 correct · 0/4 checked