Code of the Day
IntermediatePointers

Pointers to pointers

Work with double pointers (int **) in C — understanding argv, functions that modify pointer variables, and multi-level indirection.

CIntermediate10 min read
Recommended first
By the end of this lesson you will be able to:
  • Declare and use an int ** variable
  • Explain why a pointer to a pointer is needed to modify a pointer from a function
  • Interpret the argv parameter of main as a char **
  • Trace three levels of indirection through a diagram

A pointer to a pointer is exactly what it sounds like: a variable that holds the address of another pointer. Double pointers (**) come up in three main situations: when a function needs to modify a pointer variable in its caller, when representing arrays of strings (like argv), and when building dynamic data structures like linked lists and trees.

Why you need a pointer to a pointer

Consider: you want a function that sets a pointer to point somewhere new. The naive approach fails:

#include <stdio.h>

void set_to_five(int *p, int *new_target) {
    p = new_target; /* modifies p (a local copy of the pointer) -- caller's pointer unchanged */
}

int main(void) {
    int x = 42;
    int *ptr = &x;

    int five = 5;
    set_to_five(ptr, &five);

    printf("%d\n", *ptr); /* still 42 -- ptr was not changed */
    return 0;
}

ptr is passed by value — the function gets a copy of the pointer. Reassigning p inside the function does not change ptr in main.

To modify ptr from a function, pass its address (&ptr) — a pointer to the pointer:

void set_pointer(int **pp, int *new_target) {
    *pp = new_target; /* write a new address into the pointer pp points to */
}

int main(void) {
    int x = 42;
    int *ptr = &x;

    int five = 5;
    set_pointer(&ptr, &five); /* pass address of ptr */

    printf("%d\n", *ptr); /* 5 -- ptr now points to five */
    return 0;
}

pp holds the address of ptr. *pp = new_target writes new_target into ptr.

argv — the classic double pointer

The main function's full signature:

int main(int argc, char **argv) {

argv is a char **: a pointer to the first element of an array of char * pointers, each of which points to a command-line argument string.

argv ──► [ptr0] ──► "program_name\0"
         [ptr1] ──► "first_arg\0"
         [ptr2] ──► "second_arg\0"
         [NULL]
#include <stdio.h>

int main(int argc, char **argv) {
    printf("Program: %s\n", argv[0]);
    printf("Arguments:\n");
    for (int i = 1; i < argc; i++) {
        printf("  argv[%d] = \"%s\"\n", i, argv[i]);
    }
    return 0;
}

Run as ./program hello world prints:

Program: ./program
Arguments:
  argv[1] = "hello"
  argv[2] = "world"

argv[argc] is guaranteed to be NULL — you can also iterate as while (*argv) by incrementing the pointer.

Three levels: int ***

Triple pointers are rare but arise in certain data structures. The key is to work level by level:

int value = 42;
int *p1 = &value;  /* points to value */
int **p2 = &p1;    /* points to p1 */
int ***p3 = &p2;   /* points to p2 */

printf("%d\n", ***p3); /* 42 -- dereference three times */

A practical example: allocate inside a function

Dynamic memory allocation (covered in the next module) often requires double pointers:

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

void allocate_array(int **arr, int n) {
    *arr = malloc(n * sizeof(int));
    if (*arr == NULL) { return; }
    for (int i = 0; i < n; i++) {
        (*arr)[i] = i * 10;
    }
}

int main(void) {
    int *data = NULL;
    allocate_array(&data, 5);
    if (data != NULL) {
        for (int i = 0; i < 5; i++) {
            printf("%d ", data[i]); /* 0 10 20 30 40 */
        }
        printf("\n");
        free(data);
    }
    return 0;
}

malloc is introduced properly in the Memory Management module. For now, notice that the function receives &data (a int **) and writes the allocated pointer into *arr, which modifies data in main.

Where to go next

Next: const pointers — how const interacts with pointers, and the difference between a pointer to const data and a const pointer.

Finished reading? Mark it complete to track your progress.

On this page