View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All

Unlock the Power of Pointer to Pointer in C: Master Complex Memory Management

Updated on 30/04/20253,637 Views

When working with complex data structures in C, having a strong grip over pointers becomes crucial. Just like mastering the Assignment Operator in C simplifies data manipulation, understanding advanced pointer concepts brings greater control over memory. Among these, the Pointer to Pointer in C stands out as a powerful feature. 

Whether managing dynamic memory or handling multi-dimensional arrays, learning how to use Pointer to Pointer efficiently can save time and prevent common programming mistakes. In this guide, we will walk through the concept, syntax, applications, and best practices in a way that even beginners can understand with ease. You can also explore our in-depth Software engineering courses.

What Is a Pointer to Pointer in C?

In C programming, a pointer is a variable that stores the address of another variable. Now, imagine a scenario where you need to store the address of a pointer itself. This is where the Pointer to Pointer in C comes into play. It is simply a pointer that points to another pointer, creating a two-level chain of indirection.

To visualize, think of a building: the first pointer is like a receptionist who knows the location of the manager, while the second pointer knows how to find the receptionist. This level of referencing is extremely useful when working with dynamic memory, complex structures, or passing large data structures to functions efficiently.

Take your software engineering skills to the next level with these top programs:

What Is the Syntax of Pointer to Pointer in C?

The syntax of a pointer to pointer in C uses two asterisks (**) during declaration. It allows you to reference a pointer that points to another pointer, creating a two-level indirection.

Syntax:

data_type **pointer_name;
  • data_type is the type of variable being pointed to.
  • pointer_name is the name of the double pointer.

Declaration Example:

int **ptr;

In this example, ptr is a pointer to another pointer that eventually points to an integer.

Explanation:

This declaration sets up a structure where ptr holds the address of another pointer. That second pointer, in turn, holds the address of an integer. This is commonly used in advanced memory handling tasks, such as dynamic 2D arrays or passing the address of a pointer to a function.

Example:

#include <stdio.h>
int main() {
    int num = 100;
    int *p = &num;
    int **pp = &p;
    printf("Value of num: %d\n", num);
    printf("Value via single pointer: %d\n", *p);
    printf("Value via double pointer: %d\n", **pp);
    return 0;
}

Output:

Value of num: 100

Value via single pointer: 100

Value via double pointer: 100

Explanation:

  • num holds the value 100.
  • p points to num.
  • pp points to p. Using **pp, we access the original value stored in num. This technique becomes essential when managing memory dynamically or modifying pointers inside functions.

What Is the Syntax of Triple Pointer in C?

Triple pointers add another layer of indirection. You use three asterisks (***) in the declaration. They are rarely needed, but they can help in advanced use cases such as handling arrays of pointers to pointers or simulating pass-by-reference for double pointers.

Syntax:

data_type ***pointer_name;
  • data_type is the base type (e.g., int).
  • pointer_name is the name of the triple pointer.

Declaration Example:

int ***ptr3;

Explanation:

Here, ptr3 is a pointer to a pointer to a pointer. Ultimately, it allows access to an integer through three levels of indirection. This structure is helpful in scenarios such as dynamically allocated 3D arrays or modifying double pointers inside functions.

Example:

#include <stdio.h>
int main() {
    int val = 300;
    int *p = &val;
    int **pp = &p;
    int ***ppp = &pp;
    printf("Original value: %d\n", val);
    printf("Via single pointer: %d\n", *p);
    printf("Via double pointer: %d\n", **pp);
    printf("Via triple pointer: %d\n", ***ppp);
    return 0;
}

Output:

Original value: 300

Via single pointer: 300

Via double pointer: 300

Via triple pointer: 300

Explanation:

  • val is an integer.
  • p points to val.
  • pp points to p.
  • ppp points to pp. Accessing the value through ***ppp shows how triple pointers traverse three levels of indirection to reach the actual data. This pattern can be powerful but should be used carefully due to its complexity.

Learn more about function pointers in our guide.

How to Declare a Pointer to Pointer in C?

Declaring a pointer to pointer in C is simple. You place two asterisks (**) before the pointer name during declaration. This creates two levels of indirection.

Syntax:

data_type **pointer_name;
  • data_type is the type the final pointer should point to.
  • pointer_name is the variable that holds the address of another pointer.

Declaration Example:

int **pptr;

Explanation:

Here, pptr is a double pointer. It can hold the address of another pointer of type int *. This lets you create pointer chains, which are useful in dynamic memory, function callbacks, and multi-level data structures.

Example:

#include <stdio.h>
int main() {
    int value = 30;
    int *ptr = &value;
    int **pptr = &ptr;
    printf("Value: %d\n", value);
    printf("Using single pointer: %d\n", *ptr);
    printf("Using double pointer: %d\n", **pptr);
    return 0;
}

Output:

Value: 30

Using single pointer: 30

Using double pointer: 30

Explanation:

  • value holds the integer 30.
  • ptr is a pointer to value.
  • pptr is a double pointer pointing to ptr.

When you dereference *ptr, you get the value directly. When you dereference **pptr, you move through both layers and still access the same integer value. This layered pointer logic is critical when you want a function to update a pointer defined outside its scope.

How Does Pointer to Pointer in C Work Internally?

Understanding how pointer to pointer works in C helps you master multi-level memory access. A double pointer stores the address of another pointer, which in turn stores the address of a value.

Let’s look at an example:

Example:

#include <stdio.h>
int main() {
    int value = 10;
    int *ptr = &value;
    int **pptr = &ptr;
    printf("Value directly: %d\n", value);
    printf("Value via single pointer: %d\n", *ptr);
    printf("Value via double pointer: %d\n", **pptr);
    return 0;
}

Output:

Value directly: 10

Value via single pointer: 10

Value via double pointer: 10

Explanation:

  • value holds the integer 10.
  • ptr points to value, i.e., ptr = &value.
  • pptr points to ptr, i.e., pptr = &ptr.

When you use:

  • *pptr, it gives the value stored in ptr, which is the address of value.
  • **pptr, it goes one level deeper and accesses the actual value 10.

This layered access allows functions to modify pointers and dynamic structures indirectly.

You can visualize it like this:

  • value → holds 10
  • ptr → holds address of value
  • pptr → holds address of ptr

Such internal behavior is extremely useful when managing dynamic memory, nested structures, or returning modified pointers from functions.

This concept also plays a vital role when handling array of pointers in C, especially in scenarios involving strings or dynamic 2D arrays.

What Is the Size of Pointer to Pointer in C?

The size of a pointer to pointer in C is based on the system architecture, not the type it points to. Whether it's a single, double, or triple pointer, the size remains the same on a given platform.

On a 32-bit system, the size of any pointer is usually 4 bytes.On a 64-bit system, the size becomes 8 bytes.

Let’s check the size using a simple example.

Example:

#include <stdio.h>
int main() {
    int **pptr;
    printf("Size of pointer to pointer: %lu\n", sizeof(pptr));
    return 0;
}

Output (on a 64-bit system):

Size of pointer to pointer: 8

Explanation:

  • pptr is a double pointer that stores the address of another pointer.
  • Despite being a pointer to pointer, it still holds a memory address like any other pointer.
  • Therefore, its size is the same as any pointer—8 bytes on 64-bit, 4 bytes on 32-bit.
  • This size does not depend on whether the pointer refers to an int, char, or a structure.

Correct memory size assumptions prevent buffer overflows and segmentation faults in complex programs.

How to Use Pointer to Pointer in C with Arrays?

Using a pointer to pointer in C with arrays gives you dynamic control over multi-dimensional data structures. It is especially useful when the size of the array is not known at compile time. Let’s explore how this works in the context of 2D arrays.

Example: Using Pointer to Pointer with a 2D Array

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

int main() {
    int rows = 3, cols = 3;
    int **arr = (int **)malloc(rows * sizeof(int *));  // Allocate memory for row pointers

    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // Allocate memory for each row
    }

    // Assigning values
    arr[0][0] = 1;
    arr[1][1] = 2;
    arr[2][2] = 3;

    printf("Value at arr[2][2] is: %d\n", arr[2][2]);

    // Freeing memory
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);

    return 0;
}

Output:

Value at arr[2][2] is: 3

Explanation:

  • arr is a pointer to pointer that stores the address of an array of pointers.
  • Each pointer in arr points to a dynamically allocated array of integers, forming a 2D matrix.
  • arr[2][2] = 3 sets a value in the third row and third column of the matrix.
  • Dynamic allocation allows you to manage memory more flexibly, especially when dimensions change during runtime.

This method is commonly used in scenarios such as matrix operations, grids, and tabular data processing in C.

Explore further in our tutorial on Dynamic Memory Allocation in C.

What Are Some Examples of Double Pointers in C?

Double pointers play a vital role in advanced memory manipulation. They allow pointer values to be modified inside functions, help in dynamic memory allocation, and simplify working with arrays of strings or multidimensional arrays.

Example of Double Pointer in C  H3

Consider this example of a function using a double pointer to modify a pointer’s value:

#include <stdio.h>

void modifyPointer(int **ptr) {
    static int value = 50;  // Use static to keep value valid after function ends
    *ptr = &value;
}

int main() {
    int *ptr;
    modifyPointer(&ptr);
    printf("Value: %d\n", *ptr);
    return 0;
}

Output:

Value: 50

Explanation:

  • modifyPointer() accepts a double pointer int **ptr.
  • Inside the function, it assigns the address of a static integer value to *ptr.
  • In main(), the original pointer ptr now points to value, so dereferencing it gives 50.
  • The static keyword ensures the memory stays valid after the function call.

Find the Size of Double Pointer in C

As previously mentioned, the size of a double pointer is the same as a regular pointer’s size, depending on the system architecture.

#include <stdio.h>
int main() {
    int **ptr;
    printf("Size of double pointer: %lu bytes\n", sizeof(ptr));
    return 0;
}

Output (on a 64-bit system):

Size of double pointer: 8 bytes

Explanation:

  • ptr is a double pointer (int **), but its size is the same as a single pointer.
  • On a 64-bit system, all pointers occupy 8 bytes regardless of what they point to.
  • This size consistency is important in dynamic memory allocation and structure padding.

Create a Dynamic 2D Array Using Pointer to Pointer

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

int main() {
    int rows = 3, cols = 3;
    int **arr = (int **)malloc(rows * sizeof(int *));  // Allocate memory for rows

    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // Allocate memory for each row
    }

    arr[0][0] = 10;

    printf("Value at arr[0][0]: %d\n", arr[0][0]);

    // Free memory
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);

    return 0;
}

Output:

Value at arr[0][0]: 10

Explanation:

  • arr is a pointer to pointer used to simulate a 2D array.
  • Memory for rows and columns is allocated dynamically using malloc.
  • arr[0][0] = 10 stores the value at the first row and column.
  • Always free the memory to avoid memory leaks.

Pass Array of Strings to Function Using Pointer to Pointer

#include <stdio.h>

void printStrings(char **arr) {
    for (int i = 0; arr[i] != NULL; i++) {
        printf("%s\n", arr[i]);
    }
}

int main() {
    char *arr[] = {"Hello", "World", NULL};
    printStrings(arr);
    return 0;
}

Output:

Hello

World

Explanation:

  • arr is an array of string literals (i.e., char *), terminated with NULL.
  • printStrings() accepts a char ** and prints each string until NULL is found.
  • This is a common pattern when working with arrays of strings or command-line arguments.

Learn more about how to use macros in C in our tutorial

How to Perform Dynamic Memory Allocation Using Pointer to Pointer in C?

Dynamic memory allocation using a pointer to pointer in C allows you to create flexible multi-dimensional arrays at runtime, where the size is determined during execution. This is particularly useful when dealing with large or unknown-sized data structures, such as matrices, or arrays in complex applications.

Example: Dynamic Memory Allocation for a 2D Array

#include <stdio.h>
#include <stdlib.h>
int main() {
    int rows = 3, cols = 3;
    int **arr = (int **)malloc(rows * sizeof(int *));  // Allocate memory for rows
    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // Allocate memory for each column
    }
    // Assigning values
    arr[0][0] = 10;
    arr[1][1] = 20;
    arr[2][2] = 30;
    printf("Value at arr[2][2]: %d\n", arr[2][2]);  // Output: 30
    // Free memory to avoid leaks
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);
    return 0;
}

Output:

Value at arr[2][2]: 30

Explanation:

  • arr is a pointer to pointer, used to simulate a 2D array.
  • First, memory is allocated for rows (the first dimension) using malloc.
  • Then, within a loop, memory is allocated for each column (cols) of every row.
  • The values 10, 20, and 30 are assigned to arr[0][0], arr[1][1], and arr[2][2], respectively.
  • After using the array, free() is called in a loop to release memory for each row, and finally for the entire array to prevent memory leaks.

This approach allows you to create flexible 2D arrays whose size is not fixed at compile time. Dynamic allocation gives you the ability to adjust the array size as needed at runtime.

What Are the Applications of Double Pointers in C?

Double pointers have several practical applications in C, particularly when dealing with complex data structures or memory management. Let's explore some key applications.

1. Application of Double Pointers in C

One of the most common uses of double pointers is in dynamic memory management. When you need to allocate memory for a multi-dimensional array or handle complex structures like linked lists or trees, double pointers come in handy.

For example, in linked list operations, double pointers help modify the head of a list from within a function.

2. Multilevel Pointers in C

Multilevel pointers extend the concept of double pointers. For instance, a triple pointer (***ptr) can be used to point to a pointer that points to another pointer, and so on. This is useful for complex data structures such as matrices or higher-dimensional arrays.

For example, using multilevel pointers can simplify operations on 3D arrays or similar structures that require more indirection.

Dive into memory management concepts with our detailed guide.

What Are the Common Mistakes with Pointer to Pointer in C?

Working with Pointer to Pointer in C can be tricky. Here are some common mistakes to avoid:

Dereferencing a NULL Pointer

Dereferencing a NULL pointer is one of the most common mistakes in C programming. A NULL pointer points to nothing, and trying to dereference it leads to undefined behavior or segmentation faults. It’s important to always check if a pointer is NULL before attempting to dereference it.

Example:

#include <stdio.h>

int main() {
    int **ptr = NULL;

    // Dereferencing NULL pointer - leads to error!
    printf("%d", **ptr);  

    return 0;
}

Output:

Segmentation Fault (core dumped)

Explanation:

  • ptr is a pointer to a pointer, and it is initialized as NULL.
  • Attempting to dereference it with **ptr leads to a segmentation fault, as ptr doesn't point to any valid memory location.
  • This causes the program to crash or behave unexpectedly. To avoid this, always check if the pointer is NULL before dereferencing it.

Incorrect Memory Allocation

Improper memory allocation for double pointers can lead to memory leaks, crashes, or undefined behavior. It's crucial to allocate memory correctly for both the pointer itself and the memory that it points to. Failing to do so can result in faulty memory handling and runtime issues.

Example:

#include <stdio.h>
#include <stdlib.h>
int main() {
    int **arr = (int **)malloc(3 * sizeof(int *));  // Allocate memory for rows
    // Correct allocation of memory for columns
    arr[0] = (int *)malloc(3 * sizeof(int));  // Allocating memory for the first row
    // Assigning values
    arr[0][0] = 10;
    printf("Value at arr[0][0]: %d\n", arr[0][0]);  // Output: 10
    // Freeing memory
    free(arr[0]);
    free(arr);
    return 0;
}

Output:

Value at arr[0][0]: 10

Explanation:

  • The first line allocates memory for 3 pointers (rows), and arr now points to an array of pointers.
  • The second line correctly allocates memory for 3 integers (columns) for the first row of the array.
  • We assign a value to arr[0][0] and print it.
  • Finally, we free the allocated memory to avoid memory leaks.

If memory were incorrectly allocated (for example, not allocating memory for each row or column), the program could crash or produce unexpected behavior. Proper memory allocation ensures that each part of the multi-dimensional array is properly handled.

Misunderstanding Pointer Levels

It’s easy to get confused with multiple pointer levels. Each pointer level adds an extra layer of indirection, which can make dereferencing complex and error-prone.

Always keep track of the pointer levels to ensure proper dereferencing.

Not Freeing Memory Properly

When working with dynamically allocated memory, it’s crucial to free the memory in the reverse order of allocation. If memory is not freed properly, it can lead to memory leaks, which consume system resources and potentially cause your application to crash or behave unexpectedly.

Example: Freeing Memory in Reverse Order

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

int main() {
    int rows = 3, cols = 3;
    int **arr = (int **)malloc(rows * sizeof(int *));  // Allocate memory for rows

    for (int i = 0; i < rows; i++) {
        arr[i] = (int *)malloc(cols * sizeof(int));  // Allocate memory for each column
    }

    // Assigning values
    arr[0][0] = 10;
    arr[1][1] = 20;
    arr[2][2] = 30;

    printf("Value at arr[2][2]: %d\n", arr[2][2]);  // Output: 30

    // Free memory in reverse order
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);

    return 0;
}

Output:

Value at arr[2][2]: 30

Explanation:

  • arr is a double pointer that is dynamically allocated memory for both rows and columns.
  • The values 10, 20, and 30 are assigned to specific locations in the 2D array.
  • After using the array, the memory is freed starting with the columns (the inner pointers), then the rows (the outer pointer), using free() in reverse order of allocation. This ensures that all allocated memory is properly released, preventing memory leaks.

Pointer-to-pointer techniques are especially useful when working with structures in C for dynamic memory allocation. You can learn more about structures in C in our detailed guide here.

What Are the Advantages of Using Pointer to Pointer in C?

Using a Pointer to Pointer in C offers several practical advantages, especially when dealing with large data structures or dynamic memory. Here are the key benefits:

  • Efficient Memory Handling: Pointer to pointer allows you to modify the original pointer from within a function, enabling efficient memory management without returning large structures.
  • Dynamic Data Structures: It is essential for creating dynamic multi-level data structures like linked lists, trees, and graphs.
  • Flexible Function Arguments: You can pass a reference to a pointer into a function, allowing the function to update the pointer’s value directly.
  • Simplifies Complex Relationships: Managing arrays of pointers or multiple levels of indirection becomes easier and more structured.

In short, using double pointers opens up a world of possibilities when you need flexibility and control over memory in C programs. Check out this detailed guide on Pointer to Pointer in C.

Conclusion

In this article, we’ve covered the concept of Pointer to Pointer in C. We explored how to declare, use, and manipulate double pointers effectively in C programming. We also looked at common mistakes and practical applications in dynamic memory allocation and 2D arrays. Mastering pointers to pointers is essential for writing efficient, memory-conscious programs in C, especially when dealing with multi-dimensional arrays or structures.

FAQs

1. What is a Pointer to Pointer in C?

A Pointer to Pointer in C is a variable that stores the address of another pointer. It allows indirect manipulation of data, useful for complex memory structures like multi-dimensional arrays or linked lists.

2. Why is Pointer to Pointer Used in C?

Pointer to Pointer is used in C for dynamic memory management and when working with multi-level data structures like arrays of pointers. It enables efficient memory allocation and manipulation, especially in large programs.

3. How Do You Declare a Pointer to Pointer in C?

To declare a Pointer to Pointer in C, use the syntax: data_type **pointer_name; This tells the compiler that the variable holds the address of another pointer which in turn points to a data variable.

4. How to Dereference a Pointer to Pointer in C?

To dereference a Pointer to Pointer in C, you use double asterisks (**). First, dereference once to access the address stored in the pointer, and then dereference again to access the data it points to.

5. What is the Size of Pointer to Pointer in C?

The size of a Pointer to Pointer in C is the same as a regular pointer. Typically, on a 64-bit system, the size is 8 bytes, reflecting the address of the pointer it stores, not the data type it points to.

6. Can You Use Pointer to Pointer for Multi-Dimensional Arrays in C?

Yes, you can use Pointer to Pointer to represent and manipulate multi-dimensional arrays. It allows dynamic memory allocation and easy access to elements in 2D arrays, making it highly effective for such structures.

7. How to Use Pointer to Pointer with 2D Arrays in C?

To use a Pointer to Pointer with a 2D array in C, dynamically allocate memory for rows and columns. You can then access each element via double dereferencing: arr[i][j] or *(*arr + i * cols + j).

8. What Are Double Pointers Used For in C?

Double pointers are commonly used for dynamic memory allocation, modifying the address of a pointer in functions, and working with multi-dimensional arrays or linked lists. They enhance flexibility and manageability in complex programs.

9. Can Pointer to Pointer be Used for String Arrays in C?

Yes, Pointer to Pointer is useful for passing arrays of strings to functions in C. It allows you to manipulate the array of string pointers efficiently, providing access to each string without creating new arrays.

10. What is a Triple Pointer in C?

A Triple Pointer in C is a pointer that points to a pointer to a pointer. It adds another level of indirection, useful in more complex structures like multi-dimensional arrays or when working with multiple levels of pointers.

11. What Are Common Mistakes with Pointer to Pointer in C?

Common mistakes include dereferencing NULL pointers, improper memory allocation, losing track of pointer levels, and not freeing dynamically allocated memory, which can result in crashes, memory leaks, or unexpected behavior in programs.

12. How Do You Pass a Pointer to Pointer to a Function?

To pass a Pointer to Pointer to a function in C, you pass the address of the pointer itself. This allows the function to modify the pointer’s value or the data it points to within the function.

13. Is Pointer to Pointer the Same as Double Pointer in C?

Yes, Pointer to Pointer and Double Pointer refer to the same concept in C. They both describe a pointer variable that holds the address of another pointer, facilitating multiple levels of indirection for complex data management.

14. What is the Advantage of Using Pointer to Pointer in C?

The main advantage of using Pointer to Pointer in C is its ability to manage multi-dimensional arrays and dynamic memory allocation effectively. It simplifies memory management in complex data structures and allows modification within functions.

15. What Happens if You Dereference a NULL Pointer to Pointer in C?

Dereferencing a NULL Pointer to Pointer in C results in undefined behavior, often leading to a segmentation fault. Always ensure that the pointer is not NULL before attempting to dereference it to avoid crashes.

image

Take a Free C Programming Quiz

Answer quick questions and assess your C programming knowledge

right-top-arrow
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
advertise-arrow

Free Courses

Start Learning For Free

Explore Our Free Software Tutorials and Elevate your Career.

upGrad Learner Support

Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918068792934

Disclaimer

1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.

2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.