For working professionals
For fresh graduates
More
5. Array in C
13. Boolean in C
18. Operators in C
33. Comments in C
38. Constants in C
41. Data Types in C
49. Double In C
58. For Loop in C
60. Functions in C
70. Identifiers in C
81. Linked list in C
83. Macros in C
86. Nested Loop in C
97. Pseudo-Code In C
100. Recursion in C
103. Square Root in C
104. Stack in C
106. Static function in C
107. Stdio.h in C
108. Storage Classes in C
109. strcat() in C
110. Strcmp in C
111. Strcpy in C
114. String Length in C
115. String Pointer in C
116. strlen() in C
117. Structures in C
119. Switch Case in C
120. C Ternary Operator
121. Tokens in C
125. Type Casting in C
126. Types of Error in C
127. Unary Operator in C
128. Use of C Language
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.
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:
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;
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 = #
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:
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;
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:
Learn more about function pointers in our guide.
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;
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:
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.
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:
When you use:
This layered access allows functions to modify pointers and dynamic structures indirectly.
You can visualize it like this:
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.
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:
Correct memory size assumptions prevent buffer overflows and segmentation faults in complex programs.
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:
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.
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.
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:
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:
#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:
#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:
Learn more about how to use macros in C in our tutorial
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:
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.
Double pointers have several practical applications in C, particularly when dealing with complex data structures or memory management. Let's explore some key applications.
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.
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.
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:
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:
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:
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
Take a Free C Programming Quiz
Answer quick questions and assess your C programming knowledge
Author
Start Learning For Free
Explore Our Free Software Tutorials and Elevate your Career.
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918068792934
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.