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.