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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)