Home C Pointers to pointers tutorial for C

Pointers to pointers tutorial for C

A pointer to pointer in C, often called a double pointer, is a pointer that holds the address of another pointer.

This can be useful for managing dynamic memory allocation, passing pointers to functions, and handling arrays of pointers.

Understanding pointer-to-pointer relationships is essential for advanced C programming, especially when working with data structures and complex memory management.

1. Declaring a Pointer to Pointer

To declare a pointer to a pointer, use **:

data_type **pointer_name;
  • data_type: The type of data the pointer ultimately points to.
  • **: Indicates this is a pointer to another pointer.

2. Basic Example of Pointer to Pointer

Here’s a basic example to illustrate how a pointer-to-pointer works by pointing to an integer through two levels of indirection.

#include <stdio.h>

int main() {
    int value = 10;
    int *ptr = &value;      // Pointer to int
    int **pptr = &ptr;      // Pointer to pointer to int

    printf("Value: %d\n", value);
    printf("Value through ptr: %d\n", *ptr);
    printf("Value through pptr: %d\n", **pptr);

    return 0;
}

Explanation:

  • ptr points to value.
  • pptr points to ptr, so **pptr dereferences pptr twice to get value.

Output:

Value: 10
Value through ptr: 10
Value through pptr: 10

3. Modifying a Variable Using Pointer to Pointer

You can modify the original variable using a pointer-to-pointer by dereferencing it twice.

#include <stdio.h>

void modifyValue(int **pptr) {
    **pptr = 20; // Modifies the value that pptr ultimately points to
}

int main() {
    int value = 10;
    int *ptr = &value;
    int **pptr = &ptr;

    printf("Original value: %d\n", value);
    modifyValue(pptr); // Pass pointer to pointer
    printf("Modified value: %d\n", value);

    return 0;
}

Explanation:

  • modifyValue receives a pointer-to-pointer and modifies value by dereferencing it twice.

Output:

Original value: 10
Modified value: 20

4. Dynamic Memory Allocation with Pointer to Pointer

A pointer-to-pointer is useful when dynamically allocating a multi-dimensional array, like a 2D array.

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

int main() {
    int rows = 2, cols = 3;
    int **matrix = (int **)malloc(rows * sizeof(int *));

    for (int i = 0; i < rows; i++) {
        matrix[i] = (int *)malloc(cols * sizeof(int));
    }

    // Initialize matrix elements
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i * cols + j + 1;
        }
    }

    // Print matrix elements
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    // Free memory
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);

    return 0;
}

Explanation:

  • matrix is a pointer-to-pointer, dynamically allocating memory for a 2D array with rows and cols.
  • Each row is dynamically allocated, and each element in the matrix is accessed as matrix[i][j].

Output:

1 2 3
4 5 6

5. Passing Pointer to Pointer to Functions

A pointer-to-pointer can be used to modify the address stored in a pointer within a function.

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

void allocateMemory(int **ptr) {
    *ptr = (int *)malloc(sizeof(int)); // Allocates memory for an int
    **ptr = 42; // Assigns a value to the allocated memory
}

int main() {
    int *ptr = NULL;

    allocateMemory(&ptr); // Pass pointer to pointer
    printf("Value: %d\n", *ptr);

    free(ptr); // Free allocated memory
    return 0;
}

Explanation:

  • allocateMemory allocates memory to ptr by modifying its address through *ptr.
  • &ptr is passed to allocateMemory to allow the function to modify ptr.

Output:

Value: 42

6. Pointer to Pointer with Arrays of Strings

When working with arrays of strings (2D character arrays), a pointer-to-pointer can be used to point to each string.

#include <stdio.h>

int main() {
    const char *fruits[] = {"Apple", "Banana", "Cherry"};
    const char **ptr = fruits; // Pointer to pointer to char

    for (int i = 0; i < 3; i++) {
        printf("Fruit %d: %s\n", i + 1, *(ptr + i));
    }

    return 0;
}

Explanation:

  • fruits is an array of const char *, and ptr is a pointer-to-pointer pointing to fruits.
  • Each element is accessed with *(ptr + i).

Output:

Fruit 1: Apple
Fruit 2: Banana
Fruit 3: Cherry

7. Using Pointer to Pointer to Return Multiple Values

A pointer-to-pointer can be used to return multiple values from a function, such as dynamically allocated memory.

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

void allocateArray(int **arr, int size) {
    *arr = (int *)malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) {
        (*arr)[i] = i + 1;
    }
}

int main() {
    int *array = NULL;
    int size = 5;

    allocateArray(&array, size);

    for (int i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");

    free(array);
    return 0;
}

Explanation:

  • allocateArray dynamically allocates memory and returns it through a pointer-to-pointer.
  • &array is passed to the function, allowing array to hold the address of the newly allocated memory.

Output:

1 2 3 4 5

8. Passing an Array of Pointers to a Function

A function can accept a pointer-to-pointer to handle arrays of pointers, such as an array of strings.

#include <stdio.h>

void printStrings(const char **strArray, int count) {
    for (int i = 0; i < count; i++) {
        printf("String %d: %s\n", i + 1, strArray[i]);
    }
}

int main() {
    const char *names[] = {"Alice", "Bob", "Charlie"};
    printStrings(names, 3);

    return 0;
}

Explanation:

  • printStrings takes a pointer-to-pointer (const char **) to print each string in an array of strings.

Output:

String 1: Alice
String 2: Bob
String 3: Charlie

9. Double Pointer with Command-Line Arguments

Command-line arguments in C are represented as an array of strings, where argv is a pointer-to-pointer.

#include <stdio.h>

int main(int argc, char **argv) {
    printf("Total arguments: %d\n", argc);

    for (int i = 0; i < argc; i++) {
        printf("Argument %d: %s\n", i, argv[i]);
    }

    return 0;
}

Explanation:

  • argc holds the number of command-line arguments, and argv is a pointer-to-pointer storing the arguments as strings.

Sample Output:

./program arg1 arg2
Total arguments: 3
Argument 0: ./program
Argument 1: arg1
Argument 2: arg2

10. Summary Table of Pointer-to-Pointer Use Cases

Use Case Description Example Code
Basic pointer to pointer Pointer holds the address of another pointer int **pptr = &ptr;
Modifying a variable Modify original variable through pointer to pointer void modify(int **pptr) { **pptr = 20; }
Dynamic 2D array allocation Allocate and manage 2D arrays dynamically int **matrix = (int **)malloc(rows * sizeof…
Memory allocation function Allocate memory in a function void allocate(int **p) { *p = malloc(…); }
Array of strings Access strings in an array const char **ptr = fruits;
Returning multiple values Return dynamically allocated memory `

void allocateArray(int **arr) { *arr = …; } | | Array of pointers as argument| Accept array of pointers in a function |void print(char **array) { … } | | Command-line arguments | Accessargvas pointer-to-pointer |int main(int argc, char **argv) { … }` |

Complete Example: Dynamic 2D Array Manipulation with Pointer to Pointer

This example demonstrates using a pointer-to-pointer to allocate, initialize, and deallocate a 2D array dynamically.

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

void allocateMatrix(int ***matrix, int rows, int cols) {
    *matrix = (int **)malloc(rows * sizeof(int *));
    for (int i = 0; i < rows; i++) {
        (*matrix)[i] = (int *)malloc(cols * sizeof(int));
        for (int j = 0; j < cols; j++) {
            (*matrix)[i][j] = i * cols + j + 1;
        }
    }
}

void printMatrix(int **matrix, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

void freeMatrix(int **matrix, int rows) {
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }
    free(matrix);
}

int main() {
    int **matrix;
    int rows = 3, cols = 4;

    allocateMatrix(&matrix, rows, cols);
    printMatrix(matrix, rows, cols);
    freeMatrix(matrix, rows);

    return 0;
}

Explanation:

  • allocateMatrix dynamically allocates memory for a 2D array using a pointer-to-pointer-to-pointer (int ***).
  • printMatrix accesses and prints each element.
  • freeMatrix deallocates memory for each row and the entire matrix.

Output:

1 2 3 4
5 6 7 8
9 10 11 12

Pointers to pointers provide essential functionality for complex memory management tasks, particularly with multi-dimensional arrays and functions that need to modify pointers.

You may also like