Skip to main content

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.

c
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 value
  • ptr stores the address of var
  • pptr stores the address of ptr

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 10
  • ptr points to var
  • pptr points to ptr

Declaring and Initializing Pointers to Pointers

c
#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 in ptr (which is the address of value)
  • **pptr gives you the value stored at the address that ptr points to (which is the actual value 100)

Modifying Values Through Double Indirection

You can modify the original value through any level of indirection:

c
#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:

c
#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:

c
#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:

c
#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

  1. Null Pointer Check: Always check if pointers are NULL before dereferencing them to avoid segmentation faults.

  2. Memory Leaks: When using dynamic memory allocation with double pointers, make sure to free all allocated memory.

  3. Initialization: Always initialize pointers, especially pointers to pointers, to avoid undefined behavior.

  4. 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! :)