Back

Understanding Pointer to Pointer in C(Double Pointer)

23 Apr 2025
6 min read

Pointers are one of the most powerful features in C programming, allowing direct memory manipulation. Understanding pointers is essential for working with dynamic memory allocation, complex data structures, and function calls. A more advanced concept is the Pointer to Pointer in C, also called a double pointer. This concept enables efficient handling of dynamic memory, function arguments, and multi-dimensional arrays.

In this article, we will explore pointer to pointer in C with examples, applications, and related topics like pointer to object in C, pointer to arrays in C, pointer to structures in C, and pointer to function in C.

What is a Pointer in C?

A pointer is a special type of variable in C that stores the memory address of another variable. Instead of holding actual data, a pointer holds the location where the data is stored. This allows for more efficient memory management and enables powerful features like dynamic memory allocation, function pointers, and data structure manipulation.

Creating Pointers

A pointer in C is created by using the asterisk * symbol, which comes next by the pointer name and type. Next, the address-of operator & is used to assign it a variable's address.

Syntax of Pointer Declaration

Use the * operator before the variable name to declare a pointer:

data_type *pointer_name;

For Example

int *ptr;

In this case, the pointer ptr is created to hold the address of an integer variable.

Example of Pointer Usage

#include <stdio.h>

int main() {
    int number = 10;        // Declare and initialize an integer variable
    int *ptr;               // Declare a pointer to an integer

    ptr = &number;          // Assign the address of 'number' to the pointer

    // Display the value and address information
    printf("Number value: %d\n", number);
    printf("Address of number: %p\n", &number);
    printf("Pointer 'ptr' stores: %p\n", ptr);
    printf("Value at the address stored in ptr: %d\n", *ptr);

    return 0;
}

Output

Number value: 10
Address of number: 0x7ffcd3920264
Pointer 'ptr' stores: 0x7ffcd3920264
Value at the address stored in ptr: 10

How to Declare a Pointer in C?

In Pointer to Pointer in C, a pointer is a variable that holds the memory address of another variable. To declare a pointer, you use the * symbol, which signifies that the variable being declared will store a memory address rather than an actual value.

Syntax for Declaring a Pointer

data_type *pointer_name;

Pointer to Array in C

In C, a pointer to an array is a pointer that holds the address of the first element of the array. When you use an array name in an expression, it automatically acts as a pointer to its first element. This allows you to indirectly access and manipulate array elements using the pointer, making operations like iteration and element modification more flexible.

Syntax for Pointer to Array

To declare a pointer to an array, you can use the following syntax:

data_type (*pointer_name)[size_of_array];

Example: Pointer to an Array

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5};  // Array of integers
    int (*ptr)[5] = &arr;         // Pointer to the array arr (size 5)

    // Accessing elements using the pointer
    printf("First element: %d\n", (*ptr)[0]);
    printf("Second element: %d\n", (*ptr)[1]);
    printf("Third element: %d\n", (*ptr)[2]);
    printf("Fourth element: %d\n", (*ptr)[3]);
    printf("Fifth element: %d\n", (*ptr)[4]);

    return 0;
}

Code Explanation

  • This code creates an array arr with 5 numbers: {1, 2, 3, 4, 5}.
  • Next, it uses int (*ptr)[5] = &arr; to generate a pointer to the entire array. This means ptr points to the entire array, not just the first element.
  • To access the elements, it uses (*ptr)[index], which gets values from the array through the pointer.
  • Thus, the program uses the pointer to print all the 5 elements.

Output

First element: 1
Second element: 2
Third element: 3
Fourth element: 4
Fifth element: 5

Complexity for Pointers to Array

Time Complexity: O(1) per element access

Space Complexity: O(n) if allocating memory for the array, otherwise O(1) for just storing the pointer.

Pointer to Structure in C

A pointer to a structure in C stores the memory address of a structure variable. Like pointers to basic data types, a pointer to a structure allows indirect access to the structure's members.By using a pointer to a structure pointer in C, you can pass structures to functions more efficiently, modify the structure data within functions, and access or update structure members dynamically.

Syntax for Pointer to Structure

struct structure_name *pointer_name;

Example: Pointer to Structure

#include <stdio.h>

// Defining a structure called 'Person'
struct Person {
    char name[50];
    int age;
};

int main() {
    // Declaring a structure variable
    struct Person person1 = {"John Doe", 30};
    
    // Declaring a pointer to the structure
    struct Person *ptr;
    
    ptr = &person1;

    // Accessing structure members using the pointer
    printf("Name: %s\n", ptr->name);  // Using '->' to access members
    printf("Age: %d\n", ptr->age);

    return 0;
}

Code Explanation

  • The code defines a structure Person with two members: name and age. The variable person1 of type Person is then created, and values are assigned to it.
  • A pointer, ptr, is created to hold the address of person1. Using the pointer, the code accesses and prints the name and age of person1 using the -> operator, which is used to access structure members through a pointer.

Output

Name: John Doe
Age: 30

Complexity for Pointers to Array

Time Complexity: O(1) for access

Space Complexity: O(n), where n is the size of the structure in memory

What is Pointer to Pointer in C or Double Pointer?

A pointer to pointer (also called a double pointer) is a pointer that stores the memory address of another pointer. It allows indirect access to a variable through multiple levels of indirection.

custom img

Syntax of Pointer to Pointer Declaration

data_type **pointer_to_pointer;

For example

int **dptr;

How Double Pointer Works?

To understand how a double pointer works, let's break it down:

1. Single Pointer

A single pointer holds the address of a variable:

int num = 10;
int *ptr = &num;

Here, ptr holds the address of num.

2. Double Pointer

A double pointer holds the address of a pointer:

int **dptr = &ptr;

Now, dptr holds the address of ptr, which in turn holds the address of num.

3. Accessing the Value

To access the value of num through dptr, you need to dereference it twice:

printf("%d", **dptr);  // Outputs 10
  • First, dereference dptr to get ptr.
  • Then, dereference ptr to get the value of num.

Algorithm for Pointer to Pointer in C

  1. Start
  2. Declare an integer variable num and assign it a value.
  3. Declare a pointer ptr that can hold the address of an integer.
  4. Declare a double pointer dptr that can hold the address of another pointer.
  5. Store the address of num in ptr.
  6. Store the address of ptr in dptr.
  7. Access the value of num using:
    • *ptr (single dereference)
    • **dptr (double dereference)
  8. Print the original value, addresses, and values using pointer and double pointer.
  9. End

Example of Pointer to Pointer Usage

#include <stdio.h>

int main() {
    int number = 10;   // A normal integer variable
    int *ptr;       // Pointer to an integer
    int **dptr;     // Pointer to pointer

    ptr = &number;     // Storing address of num in ptr
    dptr = &ptr;    // Storing address of ptr in dptr

    printf("Value of number: %d\n", number);
    printf("Address of number: %p\n", &number);
    printf("Final value accessed through dptr: %d\n", **dptr);

    return 0;
}

Explanation

This C program demonstrates how double pointers work. It starts by declaring an integer variable number with a value of 10. Then, it creates a pointer ptr that stores the address of number. Next, a double pointer dptr is declared, which stores the address of ptr. When **dptr is used, it means accessing the value stored at the address pointed to by ptr, which is in turn pointed to by dptr. So, **dptr ultimately gives the value of number. The program displays the value of num, its memory address, and verifies that **dptr retrieves the same value, confirming correct access through the double pointer.accesses the same value.

Output

Value of number: 10
Address of number: 0x7ffeefbff67c
Final value accessed through dptr: 10

Complexity for Pointers to Array

Since a double pointer is a concept, not an algorithm, it doesn't have a defined time complexity. However, when used in operations like dynamic memory allocation or 2D arrays, we can discuss complexity based on use case:

Time Complexity

  • Accessing value using double pointer (**ptr):
    • O(1) — Constant time to dereference a double pointer and access the value.
  • Initialization or iteration (like for a 2D array using double pointers):
    • O(n × m) — If you are working with a 2D array of n rows and m columns.

Space Complexity

  • Double pointer itself:
    • Takes additional memory for storing one more level of address — usually O(1).
  • In dynamic memory use cases (e.g., 2D arrays):
    • O(n + n×m) — n for row pointers and n×m for all elements.

Pointer to Pointer Memory Representation

+-----+      +-----+      +-----+
|  a  | ---> | ptr | ---> | dptr|
+-----+      +-----+      +-----+
  10        Address of 'a'   Address of 'ptr'
  • a holds the value 10.
  • ptr holds the address of ****a.
  • dptr holds the address of ****ptr.

Size of Pointer to Pointer in C

In C, a double pointer, or pointer to pointer, has the same size as a single pointer. The system architecture determines this size:​

  • 4 bytes in 32-bit systems
  • 8 bytes on 64-bit systems

Whatever data type the pointer points to, it always has the same size. For example, on the same system, int **ptr and char **ptr both take up the same amount of memory.​

The breadth of the system's memory addresses determines the size, not the data being referenced. As a result, the size of a double pointer is specified by the design of the system and is constant across various data types.

For example, on a 64-bit system:

#include <iostream>

int main() {
    int num = 10;
    int* pointer = &num;  // Pointer to num
    int** doublePointer = &pointer;  // Double pointer pointing to pointer

    // Printing the size of pointer and double pointer
    std::cout << "Size of pointer: " << sizeof(pointer) << " bytes" << std::endl;
    std::cout << "Size of double pointer: " << sizeof(doublePointer) << " bytes" << std::endl;

    return 0;
}

Output

Size of ptr: 8 bytes
Size of dptr: 8 bytes

Referencing and Dereferencing

  • Referencing: To obtain the address of a variable, use the address-of operator (&). For example, ptr = &num; assigns the address of num to ptr. Similarly, dptr = &ptr; assigns the address of ptr to dptr.​
  • Dereferencing: To access the value pointed to by a pointer, use the dereference operator (*). For a double pointer:​
    • The value of ptr, or the address of num, is provided by dptr.
    • **dptr gives the value of num.​

This implies that you can retrieve the original value that a double pointer ultimately points to by dereferencing it twice.

Application of Double Pointers in C

​Double pointers in C are useful tools that make advanced data structures and effective memory management possible. Here are a few practical applications:

1. Dynamic Memory Allocation for Multi-Dimensional Arrays: Double pointers are crucial for memory allocation in 2D arrays because they enable flexible and dynamic memory management.

2. Passing Pointers to Functions: They enable functions to modify the original pointer, which is particularly useful for dynamic memory allocation or updating data structures.​

3. Implementing Linked Data Structures: Double pointers are commonly used to implement linked lists, trees, and other hierarchical data structures, facilitating the manipulation of node pointers.​

4. Function Pointers for Callback Mechanisms: Double pointers can be used to manage arrays of function pointers in more complex situations, allowing callback and dynamic function dispatch. 

Dynamic Memory Allocation using Pointer to Pointer in C

A pointer to pointer is particularly useful when dealing with dynamic memory allocation for multi-dimensional arrays or when you need to modify the address of a pointer within a function.

In C, dynamic memory allocation is done using functions like malloc(), calloc(), and realloc(). When using a pointer to pointer, you can allocate memory for a 2D array or manage memory dynamically for complex data structures.

Example: Dynamic Memory Allocation for a 2D Array Using Pointer to Pointer

Here's an example that shows how to dynamically allocate memory for a 2D array using a pointer to pointer:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows = 3;
    int cols = 4;

    // Declare an array of numbers as the pointer to another pointer.
    int **arr;

    // Dynamically allocate memory for 'rows' number of pointers
    arr = (int **)malloc(rows * sizeof(int *));  // Allocates memory for an array of 'rows' pointers

    // Check if memory allocation was successful
    if (arr == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }

    // Dynamically allocate memory for 'cols' number of integers for each row
    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // Allocates memory for each row
        if (arr[i] == NULL) {
            printf("Memory allocation for row %d failed!\n", i);
            return 1;
        }
    }

    // Assigning values to the 2D array
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            arr[i][j] = (i + 1) * (j + 1);  // Just an example, multiply row and column indices
        }
    }

    // Printing the 2D array
    printf("2D Array:\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }

    // Free the dynamically allocated memory
    for (int i = 0; i < rows; i++) {
        free(arr[i]);  // Free memory for each row
    }
    free(arr);  // Free memory for the array of pointers

    return 0;
}

Explanation

To create a 2D array, the program begins by declaring a double pointer arr. It dynamically allocates memory for multiple pointers, with each one pointing to a row in the array. Then, it assigns memory for the columns in each row separately. This method enables flexible memory management, allowing each row of the 2D array to be handled independently.

Next, the program assigns values to each element in the 2D array using nested loops. Each element arr[i][j] is assigned the product of (i + 1) and (j + 1), effectively filling the array with a multiplication table.​

After populating the array, the program prints its contents in a matrix format. Finally, it frees the allocated memory for each row and then the array of pointers itself to prevent memory leaks.​

This approach provides flexibility in managing 2D arrays, especially when the size of the array is determined at runtime.​

Output

2D Array:
1 2 3 4 
2 4 6 8 
3 6 9 12 

Complexity for Dynamic Memory Allocation 

Time Complexity: O(rows * cols) for allocation, O(rows) for deallocation.

Space Complexity: O(rows * cols) for the array.

Pointer to Pointer as Function Argument in C

A pointer to pointer is used in C when modifying the original pointer inside a function. This is necessary because a single pointer only allows you to modify the value at the address it points to. Still, a pointer to pointer allows you to modify the pointer itself (the memory address stored in the pointer).

Syntax for Pointer to Pointer as Function Argument

return_type function_name(data_type **pointer);

Example: Modifying Pointer in a Function (Dynamic Memory Allocation)

In this example, we will dynamically allocate memory for an integer array inside a function using a pointer to pointer.

#include <stdio.h>
#include <stdlib.h>

// Function to allocate memory for an array and initialize values
void allocateAndInitialize(int **arr, int size) {
  
    *arr = (int *)malloc(size * sizeof(int));
    
    // Check if memory allocation was successful
    if (*arr == NULL) {
        printf("Memory allocation failed!\n");
        exit(1);
    }

    for (int i = 0; i < size; i++) {
        (*arr)[i] = i + 1;
    }
}

int main() {
    int *arr = NULL;  // Declare a pointer to int, initially NULL
    int size = 5;     // Size of the array to be allocated

    // Call the function with a pointer to the pointer
    allocateAndInitialize(&arr, size);

    // Print the values of the dynamically allocated array
    printf("Array values: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // Free the dynamically allocated memory
    free(arr);

    return 0;
}

Explanation

  • In main(), an integer pointer arr is declared and initialized to NULL.
  • The function allocateAndInitialize() is called with the address of arr (i.e., a pointer to a pointer).
  • Inside the function:
    • Memory is allocated using malloc() to hold 5 integers.
    • The array is initialized with values from 1 to 5.
  • Back in main(), the initialized array values are printed.
  • Finally, the dynamically allocated memory is freed using free() to avoid memory leaks.

Output

Array values: 1 2 3 4 5 

Multilevel Pointers in C

In C, multilevel pointers are pointers that point to other pointers, allowing for multiple levels of indirection. These are also referred to as double pointers, triple pointers, and so on, based on the number of indirection levels.

Triple Pointer

A triple pointer in C is a pointer that holds the address of a double pointer, which in turn holds the address of a single pointer. This creates three levels of indirection, allowing access to a variable through multiple pointers.​

Syntax of a Triple Pointer

data_type ***ptr;
  • data_type is the type of data the pointer ultimately points to.
  • ptr is the triple pointer variable.​

Pseudocode of a Triple Pointer

BEGIN
    DECLARE integer variable num
    SET num = 10

    DECLARE pointer ptr to integer
    SET ptr to address of num

    DECLARE pointer dptr to pointer to integer
    SET dptr to address of ptr

    DECLARE pointer tptr to pointer to pointer to integer
    SET tptr to address of dptr

    PRINT value at address pointed to by tptr (dereference three times)
END

Example of a Triple Pointer

#include <stdio.h>
int main() {
    int num = 10;      
    int *ptr = &num;  
    int **dptr = &ptr; 
    int ***tptr = &dptr;

    // Accessing the value of num using the triple pointer
    printf("Value of num: %d\n", ***tptr);
    return 0;
}

Code Explanation

  • num is a simple integer variable.
  • ptr is a pointer that holds the address of num.
  • The address of ptr is stored in a double pointer called dptr.
  • tptr is a triple pointer that holds the address of dptr.
  • To access the value of num through tptr, we dereference it three times: ***tptr.​

Output

Value of num: 10

Common Mistakes to Avoid When Working With Pointers

1. Using Uninitialized Pointers

If you declare a pointer without initializing it, it may dereference to undefined behaviour. Always initialize pointers before use.

2. Dereferencing NULL Pointers

Accessing memory through a NULL pointer results in a segmentation fault. Ensure pointers are not NULL before dereferencing.

3. Memory Leaks

When dynamically allocated memory is not released, memory leaks occur. When releasing memory, always use malloc or calloc in conjunction with free.

4. Dangling Pointers

After freeing memory, the pointer still holds the address of the freed memory. Assign NULL to pointers after freeing to avoid accidental use.

5. Double Free Errors

Freeing the same memory block more than once can corrupt the memory management system. Make sure that every memory block allotted is released just once.

Conclusion

Understanding how pointers to pointers in C work is essential for mastering advanced programming concepts. It allows for better control over dynamic memory, supports the creation of complex data structures, and enhances pointer operations. Becoming familiar with various pointer types such as pointers to objects in C, pointers to arrays, pointer to structures in C, and pointers to functions in C, can significantly improve your ability to write efficient and memory-optimized C programs.

Frequently Asked Questions

1. What is a pointer to pointer in C? 

A pointer to pointer is a variable that holds the address of another pointer. It allows you to modify the address stored in a pointer or dynamically allocate memory in functions.

2. How is a pointer to pointer declared in C? 

A pointer to pointer is declared using two asterisks (**), such as data_type **pointer_name;. For example, int **ptr; declares a pointer to a pointer of type int.

3. What is the use of a pointer to pointer in C? 

A pointer to pointer is used when you need to modify the original pointer within a function or dynamically allocate memory for multi-dimensional arrays or structures.

4. How do you dereference a pointer to pointer? 

To access the value pointed to by a pointer to pointer, you use two dereference operators (**). For example, **ptr accesses the value stored at the address pointed to by the pointer stored in ptr.

5. What are the benefits of using pointer to pointer? 

Using pointer to pointer allows functions to modify the original pointer, which is useful for dynamic memory allocation or when passing arrays to functions, ensuring that changes persist outside the function.

6. Can pointer to pointer be used for multi-dimensional arrays? 

Yes, pointer to pointer is commonly used to handle dynamic memory allocation for multi-dimensional arrays, where each row is a pointer to a dynamically allocated array.

7. How do you pass a pointer to pointer to a function? 

To pass a pointer to pointer to a function, you pass the address of the pointer using the & operator. For example, function(&ptr); where ptr is a pointer to pointer.

Read More Articles

Chat with us
Chat with us
Talk to career expert