Understanding Pointer to Pointer in C (Double Pointer)

Published: 26 Nov 2025 | Reading Time: 6 min read

Table of Contents

What This Blog Has for You

This comprehensive guide covers:

By the end of this guide, you will be able to use double pointers in coding situations with real-life applications and avoid common pointer errors.

Introduction

"Master the pointer, and C reveals its hidden doors."

Every C programmer hears this advice, yet pointers remain one of the most confusing topics until you understand how memory really works.

If you want to build dynamic data structures, manage memory efficiently, or work with multi-dimensional arrays, learning pointer to pointer in C is a must-have skill. Many real-world systems, from operating systems to embedded devices, depend on these concepts.

In this guide, you will explore double pointers with simple explanations, memory diagrams, and real code examples. You'll learn how they work, where they're used, and how to avoid common pointer errors so you can write safer, more powerful C programs.

What is a Pointer in C?

A pointer is a uniquely different kind of variable in C that keeps the memory address of another variable. It is like a pointer is the one, which points to the place where the data is stored. In this way, it is possible to have more efficient memory management and hence great new features like dynamic memory allocation, function pointers, and data structure manipulation can be used.

Creating Pointers

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

Syntax of Pointer Declaration

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

data_type *pointer_name;

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 considered as a variable that stores 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;

Key Takeaway

Pointers let you work directly with memory, giving your programs more speed, flexibility, and control. Once you understand how to declare, assign, and dereference them, you unlock the foundation for mastering double pointers, dynamic memory allocation, and advanced data structures in C.

Pointer to Array in C

In C, a pointer to an array is essentially a pointer that records the location of the first element of the array. An array name, when used in an expression, is by default considered a pointer to its first element. Hence, you can use the pointer to indirectly access and change the array elements, thus making the process of traversal and element updating more efficient and convenient.

Syntax for Pointer to Array

The declaration of a pointer to an array can be done by means of 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

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

Key Takeaway

It is not necessary to have a different pointer for each element; one pointer can point to the whole array. By the means of a single pointer int (*ptr)[5], you can reach every element of the array. This technique is handy when you want to transfer big arrays into functions or when you require more explicit control over the multi-dimensional arrays. The accessing of elements is still O(1), and the pointer is not taking any additional spaces other than the reference.

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. In C, when you use a pointer to a structure pointer, it allows you to do three things: first, pass the structures to functions in a more efficient way; second, change the structure data inside the functions; and third, get the structure members or change them on the fly.

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

Output

Name: John Doe
Age: 30

Complexity for Pointers to Structure

Time Complexity: O(1) for access

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

Key Takeaway

It is also possible for a pointer to point to an array of elements instead of just a single element. With the help of int (*ptr)[5], one can reach out to the whole array from a single pointer. The usage of this method is handy when you want to pass a big array to a function or when you need better control over the multi-dimensional arrays. The access to elements is still O(1), and the pointer itself is not taking any additional space apart from the reference.

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.

Declaration and Initialization of Pointer to Pointer (Double Pointer)

Understanding how to correctly declare and initialize a double pointer is essential before using it in memory allocation, function arguments, or multi-level indirection.

What is Declaration of a Double Pointer?

A double pointer is declared by using two asterisks (**) before the variable name. It informs the compiler that rather than storing a direct value, this variable will hold the address of another pointer.

Syntax of Double Pointer

pointer_data_type **variable_name;

Example

int **dptr;

Here:

Initialization

Initialization requires a pointer whose address can be stored inside the double pointer.

int var = 20;
int *ptr = &var; // single pointer holds address of var
int **dptr = &ptr; // double pointer holds address of ptr

How Initialization Works

Key Notes

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

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.

Output

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

Complexity for Double Pointers

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:

Space Complexity:

Pointer to Pointer Memory Representation

+-----+      +-----+      +-----+
|  a  | ---> | ptr | ---> | dptr|
+-----+      +-----+      +-----+
  10        Address of 'a'   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:

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.

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

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

Quick Summary

Application of Double Pointers in C

Double pointers in C are one of the great tools which make it possible to implement advanced data structures and manage memory efficiently. Some of their applications are as follows:

  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: By using them, functions get the ability to change the original pointer, which is a great feature for dynamic memory allocation or data structures updating

  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.

Functions like malloc(), calloc(), and realloc() are used in C to allocate memory dynamically. 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

Key Takeaway

Double pointers make dynamic 2D memory allocation possible by giving full control over row-by-row allocation. This approach provides flexibility, supports runtime-defined sizes, and ensures efficient memory management, as long as you remember to free every block you allocate.

Pointer to Pointer as Function Argument in C

In C, a pointer to pointer is a variable that is used for changing 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

Output

Array values: 1 2 3 4 5

Key Takeaway

When a pointer to a pointer is used as a function argument, it allows the function to change the original pointer, which is something that a regular pointer cannot do. Basically, it is necessary for operations involving dynamic memory allocation, the efficient passing of arrays, and the retention of changed values outside the function.

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;

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

Output

Value of num: 10

Note

Multilevel pointers extend indirection by letting one pointer reference another. A triple pointer (***ptr) points to a double pointer, enabling access or modification through three levels. This structure is useful in advanced memory management, nested data structures, and scenarios where deeper pointer manipulation is required.

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: If a pointer is freed but still retains the address of that memory, then it is a dangling pointer. To be safe from any unintentional access, set pointers to NULL after freeing

  5. Double Free Errors: The act of freeing the same piece of memory twice on a system may cause the memory management system to become corrupted. It is a good practice to check that a memory block that has been allocated is freed only once

Conclusion

Understanding how pointers to pointers in C work is essential for mastering advanced programming concepts. It provides more precise management of dynamic memory, makes it possible to build intricate data structures, and deepens pointer capabilities. Learning different kinds of pointers, for instance, pointers to objects in C, pointers to arrays, pointer to structures in C, and pointers to functions in C, will have a great impact on your proficiency in writing efficient and memory-optimized C programs.

Key Points to Remember

Frequently Asked Questions

1. What is a double pointer in C?

A double pointer (or pointer to pointer) is a variable that keeps the address of another pointer. This allows access to a value through multiple levels of indirection.

2. How do you declare a double pointer?

A double pointer is declared using two asterisks.

For example:

int **dptr;

3. When should I use a double pointer?

Double pointers are used when you need to modify the address stored in a pointer (such as allocating memory inside a function), for dynamic multi-dimensional arrays, or for advanced data structures like linked lists.

4. How do you dereference a double pointer?

Dereference twice:

5. What is the advantage of a double pointer?

Double pointers allow functions to change the value of a pointer passed to them, not just the data it points to. This is essential for dynamic memory allocation and for building complex data structures.

6. What is the difference between a double pointer and an array of pointers?

A double pointer (**ptr) stores the address of a pointer, while an array of pointers (e.g., int *arr[10]) is a collection of pointers stored in contiguous memory. The usage and memory layout are different.

7. Are double pointers always required for 2D arrays?

No. Static 2D arrays (e.g., int arr[3][4]) do not require double pointers. Double pointers are needed for dynamic 2D arrays where the size is determined at runtime.

8. What are common mistakes when using double pointers?

9. Can I return a double pointer from a function?

Yes. You can return a double pointer, often after dynamically allocating memory for a 2D array or other data structure.


Related Articles


About NxtWave

NxtWave is a learning platform offering industry-recognized certifications and comprehensive programming courses. Contact: [email protected] | WhatsApp: +919390111761