Nearby lessons
94 of 124C - Pointer to Pointer
A pointer to a pointer stores the address of another pointer. Learn the ** declaration, double dereferencing, and the two places it genuinely matters — modifying a caller's pointer and building dynamic 2D arrays.
Three Levels of Indirection
A normal pointer points at data. A pointer to a pointer points at a pointer:
| Expression | Holds | Type |
|---|---|---|
x | The value 42 | int |
p | The address of x | int * |
pp | The address of p | int ** |
*pp | The address of x | int * |
**pp | The value 42 | int |
Writing Through Two Levels
**pp = value reaches all the way down to x. *pp = address changes p instead:
Why It Matters — Modifying a Caller's Pointer
int * can change the value the caller's pointer points to. To change which address the caller's pointer holds, the function needs the address of the pointer itself — that is int **.The Real-World Case — Allocating in a Function
This is the pattern that makes ** unavoidable: a function that allocates memory and hands the pointer back to the caller through a parameter:
Freeing Safely Through **
A helper that frees and sets the caller's pointer to NULL in one step — impossible with a single level of indirection:
A Dynamic 2D Array
An int ** holds an array of row pointers, each pointing at its own row. This is how you build a matrix whose size is decided at runtime:
Freeing in the Right Order
free(matrix) first, every row pointer is lost and those blocks can never be released — a guaranteed memory leak. Rows first, then the outer array, always.An Array of Strings
char ** is the natural type for a list of strings — and exactly what main's argv is:
argv Is a char **
These two signatures for main are equivalent — char *argv[] as a parameter is char **argv:
Common Mistakes
- Dereferencing the wrong number of times —
*ppis a pointer,**ppis the value. - Passing
ptrwhere&ptris needed — the function then cannot repoint the caller's pointer. - Forgetting the parentheses —
*arr[i]is not(*arr)[i]. - Freeing the outer array first — leaks every row.
- Not checking each row's
malloc— one failure mid-loop leaves a half-built matrix. - Assuming a dynamic 2D array is contiguous — the rows are separate blocks, so
matrix[0][cols]is out of bounds, notmatrix[1][0].
*. "Where is the pointer that knows where the value is?" → two. If a function must change something the caller can see, it needs one more level of indirection than the thing being changed.- Syntax: int **pp; — pp holds the address of an int *.
- *pp is the inner pointer; **pp is the actual value.
- To change a caller's pointer, a function needs ** .
- char *argv[] in main is effectively a char ** .
- A dynamic 2D array is an array of row pointers.