Skip to content

Double Pointers

A double pointer is a pointer that points to another pointer. In other words, it is a pointer to a pointer.

Declaration:
int **ptr;
Why Use Double Pointers?
  • Dynamic memory allocation: Double pointers are used when we need to allocate memory dynamically to a pointer or pass a pointer to a pointer to a function.
  • Modifying original pointer: When you need to modify the original pointer inside a function, a double pointer allows the function to access and change the pointer itself, not just the value it points to.
Example: Simple Double Pointer:
#include <iostream>
using namespace std;
int main() {
int x = 10;
int *ptr = &x; // pointer to x
int **dptr = &ptr; // double pointer pointing to ptr
cout << "Value of x: " << x << endl; // Output: 10
cout << "Value of *ptr (x): " << *ptr << endl; // Output: 10
cout << "Value of **dptr (x): " << **dptr << endl; // Output: 10
cout << "Address of ptr: " << dptr << endl; // Address of ptr
cout << "Address of x: " << ptr << endl; // Address of x
}
Key Points:
  1. Single Pointer: *ptr holds the address of a variable (e.g., x).
  2. Double Pointer: **dptr holds the address of a pointer (e.g., ptr), which in turn points to a variable.
Use Case 1: Passing Pointer to a Function (Pass by Reference):
#include <iostream>
using namespace std;
void updatePointer(int **dptr) {
**dptr = 20; // Modifying the value of x through double pointer
}
int main() {
int x = 10;
int *ptr = &x;
cout << "Before update: " << *ptr << endl; // Output: 10
updatePointer(&ptr);
cout << "After update: " << *ptr << endl; // Output: 20
return 0;
}
  • The function updatePointer takes a double pointer **dptr and updates the value of x by dereferencing twice (**dptr).
Use Case 2: Dynamic Memory Allocation:
#include <iostream>
using namespace std;
int main() {
int rows = 3, cols = 4;
// Creating a 2D array using double pointers
int **arr = new int*[rows];
for (int i = 0; i < rows; i++) {
arr[i] = new int[cols]; // Allocating memory for each row
}
// Initializing the 2D array
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = i + j;
}
}
// Displaying the 2D array
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
// Deallocating memory
for (int i = 0; i < rows; i++) {
delete[] arr[i];
}
delete[] arr;
return 0;
}
Output:
0 1 2 3
1 2 3 4
2 3 4 5

Explanation:

  • We first create an array of pointers (int **arr) where each pointer points to an array (representing rows).
  • Then, each row is dynamically allocated a new array (representing columns). After using the 2D array, we deallocate memory by deleting each row and finally the array of pointers.
Key Points to Remember:
  1. Dereferencing:
    • *ptr: Accesses the value pointed to by ptr.
    • **dptr: Accesses the value pointed to by the pointer *dptr.
  2. Pointer to Pointer:
    • A double pointer is used to store the address of another pointer.
  3. Memory Allocation:
    • Dynamic memory allocation with 2D arrays requires double pointers.

Other Example:

#include <iostream>
using namespace std;
// Function to update double pointer
void update(int **p2) {
p2 = p2 + 1; // This only updates the local copy of the pointer in this function, does not affect the original p2 in main
// No change in the actual pointer that is passed from main
//*p2 = *p2 + 1; // This increments the pointer (p) that p2 points to, effectively changing where p points
// This would change the address stored in p (no effect in this specific case as it's commented)
**p2 = **p2 + 1; // This increments the actual value that p2 points to (which is `i` from main).
// Change occurs, as it modifies the value at the address `i`
}
// Function to update value through single pointer
void update(int *p) {
*p = (*p) * 2; // This doubles the value stored in the variable `i` (if this function was called for `i`)
}
// Function to increment value through double pointer
void increment(int **p) {
++(**p); // This increments the value pointed to by the pointer inside `p`
}
int main() {
int i = 5;
int* p = &i; // Pointer `p` points to variable `i`
int** p2 = &p; // Pointer `p2` points to pointer `p`
cout << endl << endl <<" Sab sahi chal rha h " << endl << endl ;
// Display the value of `i` using different ways
cout << i << endl; // Output: 5 (direct value of `i`)
cout << *p << endl; // Output: 5 (dereferencing pointer `p`)
cout << **p2 << endl; // Output: 5 (dereferencing pointer-to-pointer `p2`)
// Display the addresses of `i`, `p`, and `p2`
cout << &i << endl; // Address of `i`
cout << p << endl; // Address of `i` (value stored in pointer `p`)
cout << *p2 << endl; // Address of `i` (value stored in pointer `p`, via dereferencing `p2`)
cout << &p << endl; // Address of pointer `p`
cout << p2 << endl; // Address of pointer `p` (value stored in `p2`)
cout << endl << endl;
// Display values before calling `update`
cout << "before " << i << endl; // Output: 5 (value of `i`)
cout << "before " << p << endl; // Output: Address of `i`
cout << "before " << p2 << endl; // Output: Address of `p`
// Call the update function
update(p2); // This modifies the value of `i` through `p2`
// Display values after calling `update`
cout << "after " << i << endl; // Output: 6 (value of `i` is incremented by `update`)
cout << "after " << p << endl; // Output: Address of `i` (no change in `p`)
cout << "after " << p2 << endl; // Output: Address of `p` (no change in `p2`)
cout << endl << endl;
// Increment using a double pointer
int num = 110;
int *ptr = &num; // Pointer `ptr` points to `num`
increment(&ptr); // This increments the value of `num` using the `increment` function
cout << num << endl; // Output: 111 (value of `num` incremented by 1)
return 0;
}