Pointers to Pointers (Double Pointers) in C
A pointer to a pointer (also known as a double pointer) is a pointer that stores the address of another pointer. This allows for multiple levels of indirection, enabling pointers to manipulate other pointers. Double pointers are especially useful in scenarios where you need to modify the address that a pointer is holding, such as when working with dynamic memory, passing pointers to functions, or manipulating multidimensional arrays.
1. Introduction to Pointers to Pointers
A pointer to a pointer allows you to create multiple levels of indirection, which is essentially a pointer that points to another pointer. In simpler terms, if a pointer holds the address of a variable, a double pointer holds the address of that pointer. This can be extended to even higher levels of pointers (triple pointers, etc.), but double pointers are the most commonly used in C.
- Pointer (
*
): Points to a variable. - Double Pointer (
**
): Points to a pointer.
Syntax:
data_type **pointer_name;
For example:
int **ptr; // Pointer to an integer pointer
2. Why Use Double Pointers?
- Dynamic Memory Allocation: Double pointers are essential for managing dynamic 2D arrays or other complex data structures.
- Modifying a Pointer’s Address: When passing pointers to a function, a double pointer allows you to modify the pointer’s value itself (not just the value it points to).
- Multidimensional Arrays: Double pointers are used to handle multidimensional arrays, particularly dynamic arrays where the size is determined at runtime.
- Function Arguments: They allow you to pass a pointer to a function and modify the pointer’s value within the function.
3. How Do Double Pointers Work?
A double pointer works by storing the address of another pointer. To dereference a double pointer, you need to use the dereference operator (*
) twice to access the original value.
4. Example of a Double Pointer
Declaring and Using a Double Pointer:
#include <stdio.h>
int main() {
int a = 10; // Integer variable
int *p = &a; // Pointer to an integer
int **pp = &p; // Pointer to a pointer
// Accessing the value of a through the double pointer
printf("Value of a: %d\n", **pp); // Dereferencing twice to get the value of a
// Printing the addresses stored
printf("Address of a: %p\n", &a);
printf("Address stored in p (address of a): %p\n", p);
printf("Address stored in pp (address of p): %p\n", pp);
return 0;
}
Explanation:
a
is an integer with the value10
.p
is a pointer that stores the address ofa
.pp
is a pointer to the pointerp
, storing the address ofp
.- Dereferencing
pp
twice (**pp
) gives the value ofa
, aspp
first points top
, which in turn points toa
.
Output:
Value of a: 10
Address of a: 0x7ffeeb00d618
Address stored in p (address of a): 0x7ffeeb00d618
Address stored in pp (address of p): 0x7ffeeb00d620
5. Double Pointers and Functions
Double pointers are often used to pass pointers to functions where the function needs to modify the original pointer itself, not just the value it points to.
Example: Modifying a Pointer in a Function
#include <stdio.h>
#include <stdlib.h>
void allocateMemory(int **ptr) {
// Allocate memory for an integer
*ptr = (int *)malloc(sizeof(int));
// Assign a value to the allocated memory
**ptr = 100;
}
int main() {
int *p = NULL; // Pointer to an integer, initially NULL
// Pass the address of p to the function
allocateMemory(&p);
// Check the value stored in the allocated memory
printf("Value in dynamically allocated memory: %d\n", *p);
// Free the allocated memory
free(p);
return 0;
}
Explanation:
- The function
allocateMemory()
takes a double pointer (int **ptr
) as an argument. - Inside the function, memory is allocated using
malloc()
, and the pointerp
inmain()
is updated to point to this newly allocated memory. - By passing the address of
p
(&p
), the function can modify the pointerp
itself, not just the value it points to.
Output:
Value in dynamically allocated memory: 100
6. Double Pointers and Dynamic Multidimensional Arrays
Double pointers are used to dynamically allocate memory for 2D arrays where the size of the array is determined at runtime.
Example: Dynamic 2D Array Allocation Using Double Pointers
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **arr;
// Allocate memory for rows (array of pointers)
arr = (int **)malloc(rows * sizeof(int *));
// Allocate memory for each column in each row
for (int i = 0; i < rows; i++) {
arr[i] = (int *)malloc(cols * sizeof(int));
}
// Initialize and print the 2D array
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = i * cols + j; // Assign values
printf("%d ", arr[i][j]); // Print values
}
printf("\n");
}
// Free the allocated memory
for (int i = 0; i < rows; i++) {
free(arr[i]);
}
free(arr);
return 0;
}
Explanation:
arr
is a double pointer, used to dynamically allocate memory for a 2D array withrows
rows andcols
columns.- Memory is allocated for each row using a loop, and then each row is further assigned a block of memory for the columns.
- After using the array, the memory is freed properly.
Output:
0 1 2 3
4 5 6 7
8 9 10 11
7. Important Points to Remember
- Dereferencing: You need to use the dereference operator (
*
) twice to access the value stored at the address pointed to by a double pointer.
**pp
gives the value pointed to by the pointerp
, which itself is pointed to bypp
.
- Modifying a Pointer: Double pointers allow you to modify the pointer itself, not just the value it points to. This is useful when passing pointers to functions.
- Dynamic Memory Management: Double pointers are often used to dynamically allocate and manage 2D arrays or other complex data structures that require multiple levels of indirection.
- Freeing Memory: When using double pointers to manage dynamic memory, make sure to free all allocated memory properly. Free each row before freeing the array of pointers.
Pointers to pointers (double pointers) are a powerful feature in C that allows for multiple levels of indirection, enabling you to manipulate pointers themselves and work with dynamic memory more flexibly. They are essential when working with dynamic data structures, passing pointers to functions, or managing multidimensional arrays. Mastering double pointers can help you write more efficient and complex C programs, particularly when working with dynamic memory allocation.