C Pointer to Pointer
In C programming, a pointer is a variable that stores the memory address of another variable. Building upon this concept, C allows you to create pointers to pointers, also known as double pointers or multi-level indirection.
What is a Pointer to Pointer?
A pointer to pointer is exactly what it sounds like: a pointer that stores the address of another pointer. This creates a second level of indirection.
int var; // A normal integer variable
int *ptr; // A pointer to an integer
int **pptr; // A pointer to a pointer to an integer
In this hierarchy:
var
stores an integer valueptr
stores the address ofvar
pptr
stores the address ofptr
Memory Visualization
To understand how pointer to pointer works, let's visualize the memory:
Memory Address Variable Value
-------------- -------- -----
0x1000 var 10
0x2000 ptr 0x1000 (address of var)
0x3000 pptr 0x2000 (address of ptr)
With this structure:
var
contains the value 10ptr
points tovar
pptr
points toptr
Declaring and Initializing Pointers to Pointers
#include <stdio.h>
int main() {
int value = 100; // An integer variable
int *ptr; // A pointer to an integer
ptr = &value; // ptr now holds the address of value
int **pptr; // A pointer to a pointer to an integer
pptr = &ptr; // pptr now holds the address of ptr
printf("Value: %d\n", value);
printf("Value via ptr: %d\n", *ptr);
printf("Value via pptr: %d\n", **pptr);
return 0;
}
Output:
Value: 100
Value via ptr: 100
Value via pptr: 100
Dereferencing Pointer to Pointer
To access the value that a pointer to pointer ultimately points to, you need to dereference it twice:
*pptr
gives you the value stored inptr
(which is the address ofvalue
)**pptr
gives you the value stored at the address thatptr
points to (which is the actual value 100)
Modifying Values Through Double Indirection
You can modify the original value through any level of indirection:
#include <stdio.h>
int main() {
int value = 100;
int *ptr = &value;
int **pptr = &ptr;
printf("Original value: %d\n", value);
// Modify via single pointer
*ptr = 200;
printf("After modifying through ptr: %d\n", value);
// Modify via double pointer
**pptr = 300;
printf("After modifying through pptr: %d\n", value);
return 0;
}
Output:
Original value: 100
After modifying through ptr: 200
After modifying through pptr: 300
Practical Applications
1. Function Parameters
Pointers to pointers are often used when a function needs to modify a pointer:
#include <stdio.h>
#include <stdlib.h>
// Function that allocates memory and assigns it to the pointer
void allocateMemory(int **ptr, int size) {
*ptr = (int*)malloc(size * sizeof(int));
}
int main() {
int *dynamicArray = NULL;
// Pass the address of the pointer
allocateMemory(&dynamicArray, 5);
// Use the allocated memory
if (dynamicArray) {
dynamicArray[0] = 10;
printf("First element: %d\n", dynamicArray[0]);
free(dynamicArray); // Don't forget to free!
}
return 0;
}
2. Arrays of Strings
Arrays of strings in C are typically implemented as arrays of pointers to characters:
#include <stdio.h>
int main() {
// Array of pointers to char (array of strings)
char *names[] = {
"Alice",
"Bob",
"Charlie"
};
// names is essentially char **
char **ptr = names;
for (int i = 0; i < 3; i++) {
printf("Name %d: %s\n", i+1, ptr[i]);
}
return 0;
}
3. Dynamically Allocated 2D Arrays
Pointers to pointers are used to implement dynamically allocated multidimensional arrays:
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **matrix;
// Allocate memory for rows
matrix = (int**)malloc(rows * sizeof(int*));
// Allocate memory for each row
for (int i = 0; i < rows; i++) {
matrix[i] = (int*)malloc(cols * sizeof(int));
}
// Initialize the matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// Print the matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%2d ", matrix[i][j]);
}
printf("\n");
}
// Free memory
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Common Pitfalls and Best Practices
-
Null Pointer Check: Always check if pointers are NULL before dereferencing them to avoid segmentation faults.
-
Memory Leaks: When using dynamic memory allocation with double pointers, make sure to free all allocated memory.
-
Initialization: Always initialize pointers, especially pointers to pointers, to avoid undefined behavior.
-
Complexity: While pointers to pointers offer powerful capabilities, they can make code harder to read and maintain. Use them only when necessary.
Summary
- A pointer to pointer (double pointer) stores the address of another pointer.
- To access the final value, you need to dereference twice:
**pptr
. - Double pointers are useful for function parameters that need to modify pointers, arrays of strings, and dynamically allocated multidimensional arrays.
- Handle with care: always check for NULL pointers and manage memory properly.
Understanding pointers to pointers is a significant milestone in mastering C programming and is essential for advanced memory management techniques.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)