CPointers to Pointers

Pointers to Pointers

A pointer holds the address of a variable. Since a pointer is itself a variable stored somewhere in memory, it has its own address — and nothing stops you from storing that address in yet another pointer. That is a pointer to a pointer, declared with two asterisks: int **pp;. It looks intimidating the first time you see it, but the idea is simple: pp points to something that itself points to an int.

C
#include <stdio.h>

int main(void) {
    int value = 42;
    int *p = &value;    // p points to value
    int **pp = &p;      // pp points to p

    printf("value = %d\n", value);
    printf("*p    = %d\n", *p);
    printf("**pp  = %d\n", **pp);

    return 0;
}
value = 42
*p    = 42
**pp  = 42
Why would you need this?

The most common reason to reach for a pointer-to-pointer is that a function needs to hand back a pointer value to its caller, and C functions only return one value directly. If a function needs to both report success/failure AND produce a freshly allocated pointer, the pointer has to travel out through an output parameter — and to modify the caller's pointer variable, the function needs the address of that pointer, i.e. a pointer to a pointer.

This is exactly the same reason you pass &x to a function that needs to modify an int x — except here the "thing being modified" is itself a pointer.

Worked example: an allocate() helper

Consider a function that allocates memory for an integer, fills it with a value, and reports back the new pointer through an output parameter:

C
#include <stdio.h>
#include <stdlib.h>

// out is a pointer to the caller's pointer variable.
// Writing *out = ... changes what the caller's pointer points to.
int allocate(int **out, int initial_value) {
    int *p = malloc(sizeof(int));
    if (p == NULL) {
        return 0; // failure
    }
    *p = initial_value;
    *out = p;       // hand the new address back to the caller
    return 1;       // success
}

int main(void) {
    int *result = NULL;

    if (allocate(&result, 100)) {
        printf("allocated value = %d\n", *result);
        free(result);
        result = NULL;
    } else {
        printf("allocation failed\n");
    }

    return 0;
}
allocated value = 100
Why not just return the pointer?
Here we could have returned `int *` directly and used `NULL` to signal failure. The `int **` style becomes essential once the function already needs its return value for something else — most commonly a status/error code — and still needs to produce a pointer as a side effect.
Arrays of pointers: `char **`

char ** shows up constantly in real C code because it is how an array of strings is represented: each char * in the array points to one string, and a char ** points to the first char * in that array. This is precisely the type of argv in int main(int argc, char *argv[])argv is really a char **. We cover this in detail in the command-line arguments and array of strings pages.

Multiple levels of dereferencing, multiple ways to go wrong
Every extra `*` you add is another place a NULL or dangling pointer can hide. Before writing `**pp`, make sure both `pp` and `*pp` are valid, non-NULL pointers — dereferencing a NULL or uninitialized pointer at any level is undefined behavior and a common source of crashes.
  • int **pp is a pointer to a pointer to an int; *pp gives you the int *, **pp gives you the int.

  • The classic use case is an output parameter: a function receives the address of the caller's pointer so it can set that pointer for them.

  • char ** is how C represents an array of strings, including argv in main.

  • Check every level of a multi-level pointer before dereferencing it — a NULL at any level crashes the same way.