Foundations — Pointer to pointer
Before you can safely read a line like this:
int x = 42; // an int
int *p = &x; // p is a pointer to int, holds x's address
int **pp = &p; // pp is a pointer to pointer, holds p's address…you must be able to read every piece of every line without guessing. This page takes each symbol and concept the parent uses and builds it from nothing — plain words, a picture, and the reason the topic needs it. Read top to bottom; each item leans on the one above it. We introduce the byte and the box first, then a value, then a pointer, then a pointer to pointer, so nothing is ever used before it is built.
1. Memory as a street of numbered boxes
Everything in this topic sits on one mental picture: computer memory is a long row of numbered boxes. Each box has a permanent number called its address.

What the figure shows: five memory boxes (bytes) in a row. Four hold ... (some other data); the highlighted middle box holds the value 42. Under each box is its address (0x0FE, 0x0FF, 0x100, …). The yellow arrow marks that the value 42 sits at address 0x100. Notice: the number under the box (address) and the number inside the box (value) are two completely different things.
Why the topic needs it: a pointer's whole job is to store an address. If you don't picture memory as numbered boxes, the words "pointer holds an address" mean nothing. See Pointers basics for the single-level version.
2. The type — how many boxes, and how to read them
Before we name any box, we need the idea that tells the computer what kind of thing a box holds.
Why the topic needs it: every pointer type in the parent (int *, int **, char **argv) is built by attaching stars to a base type. You cannot read int ** until you know what the int part promises.
3. A variable and its name
x— the human name for the box.0x100— the box's address (chosen by the computer, not you).42— the value inside.
The name x and the address 0x100 refer to the same box — one is for humans, one is for the machine.
Why the topic needs it: pointers store the addresses of variables. p = &x only makes sense once x is a real named box with a real address to point at.
4. Indirection — the whole idea in one word
Before any more symbols, name the idea that drives the entire topic.
Why the topic needs it: "pointer to pointer" literally means "two levels of indirection." Every & you add is one more level up; every * you follow is one level down.
5. & — the address-of operator
Picture it as pointing at the box and reading its number off the label.
Why the topic needs it: to make a pointer point somewhere, you must hand it an address — and & is the only way to obtain one from a variable.
6. * in a declaration — "this is a pointer"
Here is the trap that confuses everyone: * means two different things depending on where it appears.

What the figure shows: two boxes. The pink box on the left is p (address 0x200); its value is 0x100. The yellow box on the right is x (address 0x100); its value is 42. A chalk arrow runs from p's box to x's box, because p's stored value is x's address. "Following the arrow" and "reading the address stored in p" are the same act.
Why the topic needs it: a "pointer to pointer" is built by adding one more *. If you can't read one *, two will be gibberish.
7. * in an expression — dereference, "follow the arrow"
Why the topic needs it: *pp and **pp in the parent are dereferences. Each * is one "follow."
8. & and * are opposites — the wrap/peel pair

What the figure shows: three stacked labels — the value x = 42 at the bottom, its address &x = 0x100 in the middle, and "address of that, &p" at the top. The pink arrow points upward (& wraps to a higher address level); the blue arrow points downward (* peels back to the value). They are mirror moves on the same ladder.
This single ladder picture is the whole engine of the topic. Adding * to a type stacks more arrows to follow; adding & when you assign stacks more addresses to store.
9. Stacking one more level — the pointer to pointer
Now every piece is earned, we can read the parent's key line — and x and p already exist from the code above, so pp has something real to point at.
int x = 42; // int at 0x100, value 42
int *p = &x; // int* at 0x200, value 0x100
int **pp = &p; // int** at 0x300, value 0x200
What the figure shows: three boxes left to right — pp (blue, at 0x300, value 0x200), p (pink, at 0x200, value 0x100), x (yellow, at 0x100, value 42). Two arrows chain them: pp → p (labelled *pp) and p → x (the second *). Following both arrows lands on 42, which is what **pp does.
Reading the declaration with the tools above:
int **pp— declaration*(×2): "ppis a pointer to a pointer to int."pp = &p—&wraps:ppstoresp's address (0x200).*pp— one dereference (peel one): follow top, which is anint *— still a pointer, not the value yet.**pp— two dereferences (peel twice): follow top, then followptox— the actual value42.
Recall Why exactly two
* to reach the value?
Because there are two arrows between pp and x. One * crosses one arrow (lands on p); the second * crosses the next (lands on x). The number of * equals the number of arrows you must cross — two-arrow chain, two dereferences.
10. NULL — the note that points nowhere
Why the topic needs it: the parent's crashes (**pp after *pp = NULL) come from trying to follow an arrow that leads nowhere. Following a NULL arrow means walking to address 0, which holds no valid box.
11. Passing by value — why functions can't change your pointer
This is the deep reason int ** exists (parent's WHY case 1). To let a function change your pointer q, you can't hand it q (a copy) — you hand it &q (the address of q), which has type int **. Full treatment in Pass by value vs pass by reference.
Prerequisite map
The diagram below shows how each foundation feeds the next. Read this paragraph as the full description in case the diagram does not render: Memory as numbered boxes is built from the byte (the smallest box) and gives every box an address. A type (like int) says how many bytes a value uses and how to read them. A variable is a named box, built from both the address idea and the type idea. The & (wrap up) operator reads a variable's address; the * (peel down) operator follows an address back to a value. A pointer is built from type + & + * (it stores an address). Stacking one more &/* level on a pointer produces a pointer to pointer — the two-arrow chain. A pointer can also be set to NULL (points nowhere), and the copying rule of a variable gives pass by value. The pointer-to-pointer, NULL, and pass-by-value ideas are the three that feed directly into the parent topic 5.1.12.
Equipment checklist
You are ready for the parent note when you can answer each of these without peeking. Each line is written as Question ::: Answer — cover everything right of the ::: and check yourself.
What is a byte?
What does the 0x prefix mean on an address like 0x100?
What is an address in one sentence?
What is the difference between the value 42 and the address 0x100?
What does the type int tell the compiler?
What is a variable?
What is indirection?
What does &x give you?
In int *p = &x;, what does the * mean?
In *p = 5;, what does the * mean?
What does the symbol ≡ mean in *p ≡ x?
Why are & and * opposites, and when do you need parentheses?
What does int **pp hold?
Why does it take two * to read the final value through pp?
What does NULL mean and why is dereferencing it dangerous?
Why must you pass &q (not q) to let a function change your pointer?
Connections
- Parent: Pointer to pointer — the topic these foundations feed into.
- Pointers basics — the single-arrow version of everything here.
- Pass by value vs pass by reference — why copies force us up a level.
- Dynamic memory allocation (malloc) — where
int **returns fresh memory. - Command line arguments (argv) —
char **argvuses this exact stacking. - Pointer arithmetic — stepping a pointer by box-size.
- 2-D arrays vs pointer to pointer — same picture, subtly different memory layout.