1. POINTER TO POINTER
The pointer to a pointer in C is used when we want to store the address of another pointer.
The first pointer is used to store the address of the variable. And the second pointer is used to
store the address of the first pointer.
That is why they are also known as double-pointers.
We can use a pointer to a pointer to change the values of normal pointers or create a variable-
sized 2-D array.
A double pointer occupies the same amount of space in the memory stack as a normal pointer.
2. Declaration of Pointer to a Pointer in C
Declaring Pointer to Pointer is similar to declaring a pointer in C.
The difference is we have to place an additional ‘*’ before the name of the pointer.
data_type_of_pointer **name_of_variable = & normal_pointer_variable;
int val = 5;
int *ptr = &val; // storing address of val to pointer ptr.
int **d_ptr = &ptr; // pointer to a pointer declared
// which is pointing to an integer.
3. Example of Double Pointer
// C program to demonstrate pointer to pointer
#include <stdio.h>
int main()
{
int var = 789;
// pointer for var
int* ptr2;
// double pointer for ptr2
int** ptr1;
// storing address of var in ptr2
ptr2 = &var;
// Storing address of ptr2 in ptr1
ptr1 = &ptr2;
// Displaying value of var using
// both single and double pointers
printf("Value of var = %dn", var);
printf("Value of var using single pointer = %dn", *ptr2);
printf("Value of var using double pointer = %dn", **ptr1);
return 0; }
4. Size of Pointer to Pointer in C
In the C programming language, a double pointer behaves similarly to a normal pointer in C.
So, the size of the double-pointer variable is always equal to the normal pointers.
We can verify this using the below C Program.
// C program to find the size of pointer to pointer
#include <stdio.h>
int main()
{
// defining single and double pointers
int a = 5;
int* ptr = &a;
int** d_ptr = &ptr;
// size of single pointer
printf(" Size of normal Pointer: %d n", sizeof(ptr));
// size of double pointer
printf(" Size of Double Pointer: %d n", sizeof(d_ptr));
return 0;
}
Output
Size of normal Pointer: 8
Size of Double Pointer: 8
5. Application of Double Pointers in C
Following are the main uses of pointer to pointers in C:
They are used in the dynamic memory allocation of multidimensional arrays.
They can be used to store multilevel data such as the text document paragraph, sentences, and word
semantics.
They are used in data structures to directly manipulate the address of the nodes without copying.
They can be used as function arguments to manipulate the address stored in the local pointer.
Multilevel Pointers in C
Double Pointers are not the only multilevel pointers supported by the C language. What if we want to
change the value of a double pointer?
In this case, we can use a triple pointer, which will be a pointer to a pointer to a pointer i.e, int ***t_ptr.
Syntax of Triple Pointer
pointer_type *** pointer_name;
6. Chain of Pointers
A pointer is used to point to a memory location of a variable. A pointer stores the address of a
variable.
Similarly, a chain of pointers is when there are multiple levels of pointers. Simplifying, a pointer points
to address of a variable, double-pointer points to a variable and so on. This is called multiple
indirections.
Syntax:
// level-1 pointer declaration
datatype *pointer;
// level-2 pointer declaration
datatype **pointer;
// level-3 pointer declaration
datatype ***pointer;
.
.
7. The level of the pointer depends on how many asterisks the pointer variable is preceded with at the
time of declaration.
Declaration:
int *pointer_1;
int **pointer_2;
int ***pointer_3;
.
.
and so on
Level of pointers or say chain can go up to N level depending upon the memory size. If you want to
create a pointer of level-5, you need to precede the pointer variable name by 5 asterisks(*) at the time
of declaration.
8. Initialization:
// initializing level-1 pointer
// with address of variable 'var'
pointer_1 = &var;
// initializing level-2 pointer
// with address of level-1 pointer
pointer_2 = &pointer_1;
// initializing level-3 pointer
// with address of level-2 pointer
pointer_3 = &pointer_2;
.
.
and so on
9. #include <stdio.h>
// C program for chain of pointer
int main()
{
int var = 10;
// Pointer level-1
// Declaring pointer to variable var
int* ptr1;
// Pointer level-2
// Declaring pointer to pointer variable *ptr1
int** ptr2;
// Pointer level-3
// Declaring pointer to double pointer **ptr2
int*** ptr3;
// Storing address of variable var
// to pointer variable ptr1
ptr1 = &var;
10. // Storing address of pointer variable
// ptr1 to level -2 pointer ptr2
ptr2 = &ptr1;
// Storing address of level-2 pointer
// ptr2 to level-3 pointer ptr3
ptr3 = &ptr2;
// Displaying values
printf("Value of variable "
"var = %dn",
var);
printf("Value of variable var using"
" pointer ptr1 = %dn",
*ptr1);
printf("Value of variable var using"
" pointer ptr2 = %dn",
**ptr2);
printf("Value of variable var using"
" pointer ptr3 = %dn",
***ptr3);
return 0;
}
11. Value of variable var = 10
Value of variable var using pointer ptr1 = 10
Value of variable var using pointer ptr2 = 10
Value of variable var using pointer ptr3 = 10
12. #include <stdio.h>
int main()
{
float var = 23.564327;
// Declaring pointer variables upto level_4
float *ptr1, **ptr2, ***ptr3, ****ptr4;
// Initializing pointer variables
ptr1 = &var;
ptr2 = &ptr1;
ptr3 = &ptr2;
ptr4 = &ptr3;
// Printing values
printf("Value of var = %fn", var);
printf("Value of var using level-1"
" pointer = %fn",
*ptr1);
printf("Value of var using level-2"
" pointer = %fn",
**ptr2);
13. printf("Value of var using level-3"
" pointer = %fn",
***ptr3);
printf("Value of var using level-4"
" pointer = %fn",
****ptr4);
return 0;
}
Value of var = 23.564327
Value of var using level-1 pointer = 23.564327
Value of var using level-2 pointer = 23.564327
Value of var using level-3 pointer = 23.564327
Value of var using level-4 pointer = 23.564327
14. Pointer to an Array
#include<stdio.h>
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int *ptr = arr;
printf("%pn", ptr);
return 0;
}
15. data_type (*var_name)[size_of_array];
Here:
data_type is the type of data that the array holds.
var_name is the name of the pointer variable.
size_of_array is the size of the array to which the pointer will point.
Example
int (*ptr)[10];
Here ptr is pointer that can point to an array of 10 integers. Since subscript have higher precedence
than indirection, it is necessary to enclose the indirection operator and pointer name inside
parentheses. Here the type of ptr is ‘pointer to an array of 10 integers.
16. // C program to understand difference between
// pointer to an integer and pointer to an
// array of integers.
#include<stdio.h>
int main()
{
// Pointer to an integer
int *p;
// Pointer to an array of 5 integers
int (*ptr)[5];
int arr[5];
// Points to 0th element of the arr.
p = arr;
// Points to the whole array arr.
ptr = &arr;
printf("p = %p, ptr = %pn", p, ptr);
17. p++;
ptr++;
printf("p = %p, ptr = %pn", p, ptr);
return 0;
}
Output
p = 0x7fff6463e890, ptr = 0x7fff6463e890
p = 0x7fff6463e894, ptr = 0x7fff6463e8a4
Here, p is pointer to 0th element of the array arr, while ptr is a pointer that points to the whole array
arr.
The base type of p is int while base type of ptr is ‘an array of 5 integers’.
We know that the pointer arithmetic is performed relative to the base size, so if we write ptr++, then
the pointer ptr will be shifted forward by 20 bytes.
The following figure shows the pointer p and ptr. The darker arrow denotes a pointer to an array.
18. .
On dereferencing a pointer expression we get a value pointed to by that pointer expression.
The pointer to an array points to an array, so on dereferencing it, we should get the array, and the
name of the array denotes the base address.
So whenever a pointer to an array is dereferenced, we get the base address of the array to which it
points
19. // C program to illustrate sizes of
// pointer of array
#include<stdio.h>
int main()
{
int arr[] = { 3, 5, 6, 7, 9 };
int *p = arr;
int (*ptr)[5] = &arr;
printf("p = %p, ptr = %pn", p, ptr);
printf("*p = %d, *ptr = %pn", *p, *ptr);
printf("sizeof(p) = %lu, sizeof(*p) = %lun",
sizeof(p), sizeof(*p));
printf("sizeof(ptr) = %lu, sizeof(*ptr) = %lun",
sizeof(ptr), sizeof(*ptr));
return 0;
}
21. Pointer to Multidimensional Arrays
1. Pointers and Two-Dimensional Arrays
In a two-dimensional array, we can access each element by using two subscripts, where the first
subscript represents the row number and the second subscript represents the column number. The
elements of 2-D array can be accessed with the help of pointer notation also. Suppose arr is a 2-D
array, we can access any element arr[i][j] of the array using the pointer expression *(*(arr + i) + j). Now
we’ll see how this expression can be derived.
Let us take a two dimensional array arr[3][4]:
int arr[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
22. Since memory in a computer is organized linearly it is not possible to store the 2-D array in rows and
columns. The concept of rows and columns is only theoretical, actually, a 2-D array is stored in row-
major order i.e rows are placed next to each other.
The following figure shows how the above 2-D array will be stored in memory.
Each row can be considered as a 1-D array, so a two-dimensional array can be considered as a
collection of one-dimensional arrays that are placed one after another.
In other words, we can say that 2-D dimensional arrays that are placed one after another.
So here arr is an array of 3 elements where each element is a 1-D array of 4 integers.
So we can say that arr points to the 0th 1-D array, arr + 1 points to the 1st 1-D array and arr + 2 points
to the 2nd 1-D array
23. arr + i Points to ith element of arr -> Points to ith 1-D array
24. Pointers and Three Dimensional Arrays
int arr[2][3][2] = { {{5, 10}, {6, 11}, {7, 12}}, {{20, 30}, {21, 31}, {22, 32}} };
In a three dimensional array we can access each element by using three subscripts.
Let us take a 3-D array- We can consider a three dimensional array to be an array of 2-D array i.e each
element of a 3-D array is considered to be a 2-D array.
The 3-D array arr can be considered as an array consisting of two elements where each element is a 2-D
array. The name of the array arr is a pointer to the 0th 2-D array.
Output
5 10
6 11
7 12
20 30
21 31
22 32
The following figure shows how the 3-D array used in the above program is stored in memory.
25. // C program to print the elements of 3-D
// array using pointer notation
#include<stdio.h>
int main()
{
int arr[2][3][2] = {
{
{5, 10},
{6, 11},
{7, 12},
},
{
{20, 30},
{21, 31},
{22, 32},
}
};
int i, j, k;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
for (k = 0; k < 2; k++)
printf("%dt", *(*(*(arr + i) + j) +k));
printf("n");
}
}
return 0;
}
26.
27. Subscripting Pointer to an Array
Suppose arr is a 2-D array with 3 rows and 4 columns and ptr is a pointer to an array of 4 integers, and
ptr contains the base address of array arr.
int arr[3][4] = {{10, 11, 12, 13}, {20, 21, 22, 23}, {30, 31, 32, 33}};
int (*ptr)[4];
ptr = arr;
28. // C program to print elements of a 2-D array
// by scripting a pointer to an array
#include<stdio.h>
int main()
{
int arr[3][4] = {
{10, 11, 12, 13},
{20, 21, 22, 23},
{30, 31, 32, 33}
};
int (*ptr)[4];
ptr = arr;
printf("%p %p %pn", ptr, ptr + 1, ptr + 2);
printf("%p %p %pn", *ptr, *(ptr + 1), *(ptr + 2));
printf("%d %d %dn", **ptr, *(*(ptr + 1) + 2), *(*(ptr + 2) + 3));
printf("%d %d %dn", ptr[0][0], ptr[1][2], ptr[2][3]);
return 0;
}
29. Dynamic Memory Allocation in C using malloc(), calloc(), free() and realloc()
Since C is a structured language, it has some fixed rules for programming.
One of them includes changing the size of an array. An array is a collection of items stored at
contiguous memory locations.
As can be seen, the length (size) of the array above is 9. But what if there is a requirement to change
this length (size)? For example,
If there is a situation where only 5 elements are needed to be entered in this array.
In this case, the remaining 4 indices are just wasting memory in this array. So there is a requirement to
lessen the length (size) of the array from 9 to 5.
Take another situation. In this, there is an array of 9 elements with all 9 indices filled.
But there is a need to enter 3 more elements in this array. In this case, 3 indices more are required. So
the length (size) of the array needs to be changed from 9 to 12.
30. This procedure is referred to as Dynamic Memory Allocation in C.
Therefore, C Dynamic Memory Allocation can be defined as a procedure in which the size of a data
structure (like Array) is changed during the runtime.
C provides some functions to achieve these tasks.
There are 4 library functions provided by C defined under <stdlib.h> header file to facilitate dynamic
memory allocation in C programming. They are:
31. malloc()
calloc()
free()
realloc()
Let’s look at each of them in greater detail.
C malloc() method
The “malloc” or “memory allocation” method in C is used to dynamically allocate a single large block
of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of
any form. It doesn’t Initialize memory at execution time so that it has initialized each block with the
default garbage value initially.
Syntax of malloc() in C
ptr = (cast-type*) malloc(byte-size)
32. For Example:
ptr = (int*) malloc(100 * sizeof(int));
Since the size of int is 4 bytes, this statement will allocate 400 bytes of memory. And, the pointer ptr
holds the address of the first byte in the allocated memory.
If space is insufficient, allocation fails and returns a NULL pointer.
33. #include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
printf("Enter number of elements:");
scanf("%d",&n);
printf("Entered number of elements: %dn", n);
// Dynamically allocate memory using malloc()
ptr = (int*)malloc(n * sizeof(int));
// Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.n");
exit(0);
}
34. else {
// Memory has been successfully allocated
printf("Memory successfully allocated using malloc.n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}
return 0;
}
35. Output
Enter number of elements: 5
Memory successfully allocated using malloc.
The elements of the array are: 1, 2, 3, 4, 5,
36. C calloc() method
“calloc” or “contiguous allocation” method in C is used to dynamically allocate the specified number of
blocks of memory of the specified type.
it is very much similar to malloc() but has two different points and these are:
It initializes each block with a default value ‘0’.
It has two parameters or arguments as compare to malloc().
Syntax of calloc() in C
ptr = (cast-type*)calloc(n, element-size);
here, n is the no. of elements and element-size is the size of each element.
For Example:
ptr = (float*) calloc(25, sizeof(float));
This statement allocates contiguous space in memory for 25 elements each with the size of the float.
37. If space is insufficient, allocation fails and returns a NULL pointer.
Example of calloc() in C
38. #include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %dn", n);
// Dynamically allocate memory using calloc()
ptr = (int*)calloc(n, sizeof(int));
39. // Check if the memory has been successfully
// allocated by calloc or not
if (ptr == NULL) {
printf("Memory not allocated.n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using calloc.n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
40. // Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
}
return 0;
}
Output
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,
41. C free() method
“free” method in C is used to dynamically de-allocate the memory. The memory allocated using
functions malloc() and calloc() is not de-allocated on their own. Hence the free() method is used,
whenever the dynamic memory allocation takes place. It helps to reduce wastage of memory by
freeing it.
Syntax of free() in C
free(ptr);
42. #include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int *ptr, *ptr1;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %dn", n);
43. // Dynamically allocate memory using malloc()
ptr = (int*)malloc(n * sizeof(int));
// Dynamically allocate memory using calloc()
ptr1 = (int*)calloc(n, sizeof(int));
// Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL || ptr1 == NULL) {
printf("Memory not allocated.n");
exit(0);
}
44. else {
// Memory has been successfully allocated
printf("Memory successfully allocated using malloc.n");
// Free the memory
free(ptr);
printf("Malloc Memory successfully freed.n");
// Memory has been successfully allocated
printf("nMemory successfully allocated using calloc.n");
// Free the memory
free(ptr1);
printf("Calloc Memory successfully freed.n");
}
return 0;
}
45. Output
Enter number of elements: 5
Memory successfully allocated using malloc.
Malloc Memory successfully freed.
Memory successfully allocated using calloc.
Calloc Memory successfully freed.
46. C realloc() method
“realloc” or “re-allocation” method in C is used to dynamically change the memory allocation of a
previously allocated memory. In other words, if the memory previously allocated with the help of
malloc or calloc is insufficient, realloc can be used to dynamically re-allocate memory. re-allocation of
memory maintains the already present value and new blocks will be initialized with the default
garbage value.
If space is insufficient, allocation fails and returns a NULL pointer.
Syntax of realloc() in C
ptr = realloc(ptr, newSize);
where ptr is reallocated with new size 'newSize’.
47. #include <stdio.h>
#include <stdlib.h>
int main()
{
// This pointer will hold the
// base address of the block created
int* ptr;
int n, i;
// Get the number of elements for the array
n = 5;
printf("Enter number of elements: %dn", n);
// Dynamically allocate memory using calloc()
ptr = (int*)calloc(n, sizeof(int));
48. // Check if the memory has been successfully
// allocated by malloc or not
if (ptr == NULL) {
printf("Memory not allocated.n");
exit(0);
}
else {
// Memory has been successfully allocated
printf("Memory successfully allocated using calloc.n");
// Get the elements of the array
for (i = 0; i < n; ++i) {
ptr[i] = i + 1;
}
49. // Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
// Get the new size for the array
n = 10;
printf("nnEnter the new size of the array: %dn", n);
// Dynamically re-allocate memory using realloc()
ptr = (int*)realloc(ptr, n * sizeof(int));
// Memory has been successfully allocated
printf("Memory successfully re-allocated using realloc.n");
50. // Get the new elements of the array
for (i = 5; i < n; ++i) {
ptr[i] = i + 1;
}
// Print the elements of the array
printf("The elements of the array are: ");
for (i = 0; i < n; ++i) {
printf("%d, ", ptr[i]);
}
free(ptr);
}
return 0;
}
51. Output
Enter number of elements: 5
Memory successfully allocated using calloc.
The elements of the array are: 1, 2, 3, 4, 5,
Enter the new size of the array: 10
Memory successfully re-allocated using realloc.
The elements of the array are: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,