Pointers in C PDF
Pointers in C PDF
https://www.geeksforgeeks.org/c-pointers/
What is a Pointer in C?
A pointer is defined as a derived data type that can store the address of other C variables or a
memory location. We can access and manipulate the data stored in that memory location using
pointers.
As the pointers in C store the memory addresses, their size is independent of the type of data they
are pointing to. This size of pointers in C only depends on the system architecture.
Syntax of C Pointers
The syntax of pointers is similar to the variable declaration in C, but we use the ( * ) dereferencing
operator in the pointer declaration.
datatype * ptr;
where
• ptr is the name of the pointer.
• datatype is the type of data it is pointing to.
The above syntax is used to define a pointer to a variable. We can also define pointers to functions,
structures, etc.
1. Pointer Declaration
2. Pointer Initialization
3. Pointer Dereferencing
1. Pointer Declaration
In pointer declaration, we only declare the pointer but do not initialize it. To declare a pointer, we
use the ( * ) dereference operator before its name.
Example
int *ptr;
The pointer declared here will point to some random memory address as it is not initialized. Such
pointers are called wild pointers.
2. Pointer Initialization
Pointer initialization is the process where we assign some initial value to the pointer variable. We
generally use the ( &: ampersand ) addressof operator to get the memory address of a variable and
then store it in the pointer variable.
Example
int var = 10;
int * ptr;
ptr = &var;
We can also declare and initialize the pointer in a single step. This method is called pointer
definition as the pointer is declared and initialized at the same time.
Example
int *ptr = &var;
Note: It is recommended that the pointers should always be initialized to some value before starting
using it. Otherwise, it may lead to number of errors.
3. Pointer Dereferencing
Dereferencing a pointer is the process of accessing the value stored in the memory address
specified in the pointer. We use the same ( * ) dereferencing operatorthat we used in the pointer
declaration.
Types of Pointers in C
Pointers in C can be classified into many different types based on the parameter on
which we are defining their types. If we consider the type of variable stored in the
memory location pointed by the pointer, then the pointers can be classified into the
following types:
1. Integer Pointers
As the name suggests, these are the pointers that point to the integer values.
Syntax
int *ptr;
2. Pointer to Array
Here:
Note: The pointer that points to the 0th element of array and the pointer that points to
the whole array are totally different. The following program shows this
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.
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
We know that the name of an array is a constant pointer that points to 0th 1-D array
and contains address 5000. Since arr is a ‘pointer to an array of 4 integers’, according
to pointer arithmetic the expression arr + 1 will represent the address 5016 and
expression arr + 2 will represent address 5032.
So we can say that arr points to the 0th 1-D array, arr + 1points to the 1st 1-D array
and arr + 2 points to the 2nd1-D array.
• Since arr + i points to ith element of arr, on dereferencing it will get ith element
of arr which is of course a 1-D array. Thus the expression *(arr + i)gives us the base
address of ith 1-D array.
• We know, the pointer expression *(arr + i) is equivalent to the subscript
expression arr[i]. So *(arr + i) which is same as arr[i] gives us the base address of ith 1-
D array.
• To access an individual element of our 2-D array, we should be able to access any
jth element of ith 1-D array.
• Since the base type of *(arr + i) is int and it contains the address of 0th element of
ith 1-D array, we can get the addresses of subsequent elements in the ith 1-D array by
adding integer values to *(arr + i).
• For example *(arr + i) + 1 will represent the address of 1st element of 1stelement of
ith 1-D array and *(arr+i)+2 will represent the address of 2nd element of ith 1-D array.
• Similarly *(arr + i) + j will represent the address of jthelement of ith 1-D array. On
dereferencing this expression we can get the jth element of the ith 1-D array.
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.
We know the expression *(arr + i) is equivalent to arr[i] and the expression *(*(arr + i) +
j) is equivalent arr[i][j]. So we can say that arr[i] represents the base address of ith 2-D
array and arr[i][j] represents the base address of the jth 1-D array.
Example
The below example demonstrates the program to print elements of 3D array using
pointers.
3. Pointer to structure
A structure pointer is defined as the pointer which points to the address of the memory
block that stores a structure known as the structure pointer. Complex data structures
like Linked lists, trees, graphs, etc. are created with the help of structure pointers. The
structure pointer tells the address of a structure in memory by pointing the variable to
the structure variable.
Example:
struct point {
int value;
};
int main()
{
struct point s;
return 0;
}
In the above code s is an instance of struct point and ptr is the struct pointer because it
is storing the address of struct point.
There are two ways to access the members of the structure with the help of a structure
pointer:
1. With the help of (*) asterisk or indirection operator and (.) dot operator.
2. With the help of ( -> ) Arrow operator.
Below is the program to access the structure members using the structure pointer with
the help of the dot operator.
struct Student {
int roll_no;
char name[30];
char branch[40];
int batch;
};
int main()
{
s1.roll_no = 27;
strcpy(s1.name, "Kamlesh Joshi");
strcpy(s1.branch, "Computer Science And Engineering");
s1.batch = 2019;
return 0;
}
Output:
1
Below is the program to access the structure members using the structure pointer with
the help of the Arrow operator. In this program, we have created a Structure Student
containing structure variable s. The Structure Student has roll_no, name, branch, and
batch.
int main()
{
ptr = &s;
// Taking inputs
printf("Enter the Roll Number of Student\n");
scanf("%d", &ptr->roll_no);
printf("Enter Name of Student\n");
scanf("%s", &ptr->name);
printf("Enter Branch of Student\n");
scanf("%s", &ptr->branch);
printf("Enter batch of Student\n");
scanf("%d", &ptr->batch);
return 0;
}
Output:
Enter the Roll Number of Student
27
Enter Name of Student
Kamlesh_Joshi
Enter Branch of Student
Computer_Science_And_Engineering
Enter batch of Student
2019
Student details are:
Roll No: 27
Name: Kamlesh_Joshi
Branch: Computer_Science_And_Engineering
Batch: 2019
4. Function pointer
In C, like normal data pointers (int *, char *, etc), we can have pointers to functions.
Following is a simple example that shows declaration and function call using function
pointer.
#include <stdio.h>
// A normal function with an int parameter
// and void return type
void fun(int a) { printf("Value of a is %d\n", a); }
int main()
{
// fun_ptr is a pointer to function fun()
void (*fun_ptr)(int) = &fun;
/* The above line is equivalent of following two
void (*fun_ptr)(int);
fun_ptr = &fun;
*/
// Invoking fun() using fun_ptr
(*fun_ptr)(10);
return 0;
}
Output
Value of a is 10
Why do we need an extra bracket around function pointers like fun_ptr in above
example?
If we remove bracket, then the expression “void (*fun_ptr)(int)” becomes “void
*fun_ptr(int)” which is declaration of a function that returns void pointer. See following
post for details.
1) Unlike normal pointers, a function pointer points to code, not data. Typically a
function pointer stores the start of executable code.
3) A function’s name can also be used to get functions’ address. For example, in the
below program, we have removed address operator ‘&’ in assignment. We have also
changed function call by removing *, the program still works.
#include <stdio.h>
// A normal function with an int parameter
// and void return type
void fun(int a) { printf("Value of a is %d\n", a); }
int main()
{
void (*fun_ptr)(int) = fun; // & removed
fun_ptr(10); // * removed
return 0;
}
Output
Value of a is 10
4) Like normal pointers, we can have an array of function pointers. Below example in
point 5 shows syntax for array of pointers.
5) Function pointer can be used in place of switch case. For example, in below
program, user is asked for a choice between 0 and 2 to do different tasks.
#include <stdio.h>
void add(int a, int b)
{
printf("Addition is %d\n", a + b);
}
void subtract(int a, int b)
{
printf("Subtraction is %d\n", a - b);
}
void multiply(int a, int b)
{
printf("Multiplication is %d\n", a * b);
}
int main()
{
// fun_ptr_arr is an array of function pointers
void (*fun_ptr_arr[])(int, int)
= { add, subtract, multiply };
unsigned int ch, a = 15, b = 10;
printf("Enter Choice: 0 for add, 1 for subtract and 2 "
"for multiply\n");
scanf("%d", &ch);
if (ch > 2)
return 0;
(*fun_ptr_arr[ch])(a, b);
return 0;
}
Output
Enter Choice: 0 for add, 1 for subtract and 2 for multiply
2
Multiplication is 150
6) Like normal data pointers, a function pointer can be passed as an argument and can
also be returned from a function.
For example, consider the following C program where wrapper() receives a void fun()
as parameter and calls the passed function.
#include <stdbool.h>
#include <stdio.h>
// A compare function that is used for searching an integer
// array
bool compare(const void* a, const void* b)
{
return (*(int*)a == *(int*)b);
}
// General purpose search() function that can be used
// for searching an element *x in an array arr[] of
// arr_size. Note that void pointers are used so that
// the function can be called by passing a pointer of
// any type. ele_size is size of an array element
int search(void* arr, int arr_size, int ele_size, void* x,
bool compare(const void*, const void*))
{
// Since char takes one byte, we can use char pointer
// for any type/ To get pointer arithmetic correct,
// we need to multiply index with size of an array
// element ele_size
char* ptr = (char*)arr;
int i;
for (i = 0; i < arr_size; i++)
if (compare(ptr + i * ele_size, x))
return i;
// If element not found
return -1;
}
int main()
{
int arr[] = { 2, 5, 7, 90, 70 };
int n = sizeof(arr) / sizeof(arr[0]);
int x = 7;
printf("Returned index is %d ",
search(arr, n, sizeof(int), &x, compare));
return 0;
}
Output
Returned index is 2
The above search function can be used for any data type by writing a separate
customized compare().
5. Double 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.
int main()
{
int var = 789;
return 0;
}
Output
Value of var = 789
Value of var using single pointer = 789
Value of var using double pointer = 789
How Double Pointer Works?
The working of the double-pointer can be explained using the above image:
• The double pointer is declared using the syntax shown above.
• After that, we store the address of another pointer as the value of this new
double pointer.
• Now, if we want to manipulate or dereference to any of its levels, we have to use
Asterisk ( * ) operator the number of times down the level we want to go.
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.
int main()
{
// defining single and double pointers
int a = 5;
int* ptr = &a;
int** d_ptr = &ptr;
return 0;
}
Output
Size of normal Pointer: 8
Size of Double Pointer: 8
Note: The output of the above code also depends on the type of machine which is
being used. The size of a pointer is not fixed in the C programming language and
it depends on other factors like CPU architecture and OS used. Usually, for a 64-
bit Operating System, the size will be 8 bytes and for a 32-bit Operating system,
the size will be 4 bytes.
Note: We can use any level pointer in C. There is no restriction about it but it
makes the program very complex and vulnerable to errors.
6. Null pointer
The Null Pointer is the pointer that does not point to any location but NULL. According
to C11 standard:
“An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare
unequal to a pointer to any object or function.”
1. To initialize a pointer variable when that pointer variable hasn’t been assigned any
valid memory address yet.
2. To check for a null pointer before accessing any pointer variable. By doing so, we can
perform error handling in pointer-related code, e.g., dereference a pointer variable only
if it’s not NULL.
3. To pass a null pointer to a function argument when we don’t want to pass any valid
memory address.
4. A NULL pointer is used in data structures like trees, linked lists, etc. to indicate the end.
Check if the pointer is NULL
It is a valid operation in pointer arithmetic to check whether the pointer is NULL. We
just have to use isequal to operator ( == ) as shown below:
ptr == NULL;
The above equation will be true if the pointer is NULL, otherwise, it will be false.
Examples of NULL Pointer in C
int main()
{
// declaring null pointer
int* ptr = NULL;
6. Void pointer
A void pointer is a pointer that has no associated data type with it. A void pointer can
hold an address of any type and can be typecasted to any type.
#include <stdio.h>
int main()
{
int a = 10;
char b = 'x';
#include <stdio.h>
int main()
{
int a = 10;
void* ptr = &a;
printf("%d", *ptr);
return 0;
}
Output
Compiler Error: 'void*' is not a pointer-to-object type
The below program demonstrates the usage of a void pointer to store the address of
an integer variable and the void pointer is typecasted to an integer pointer and then
dereferenced to access the value. The following program compiles and runs fine.
#include <stdio.h>
int main()
{
int a = 10;
void* ptr = &a;
// The void pointer 'ptr' is cast to an integer pointer
// using '(int*)ptr' Then, the value is dereferenced
// with `*(int*)ptr` to get the value at that memory
// location
printf("%d", *(int*)ptr);
return 0;
}
Output
10
Time Complexity: O(1)
Auxiliary Space: O(1)
2. The C standard doesn’t allow pointer arithmetic with void pointers. However, in
GNU C it is allowed by considering the size of the void as 1.
Example
The below C program demonstrates the usage of a void pointer to perform pointer
arithmetic and access a specific memory location. The following program compiles and
runs fine in gcc.
#include <stdio.h>
int main()
{
// Declare and initialize an integer array 'a' with two
// elements
int a[2] = { 1, 2 };
// Declare a void pointer and assign the address of
// array 'a' to it
void* ptr = &a;
return 0;
}
Output
2
Time Complexity: O(1)
Auxiliary Space: O(1)
7. Wilid pointer
Uninitialized pointers are known as wild pointers because they point to some arbitrary
memory location and may cause a program to crash or behave unexpectedly.
int main()
{
int* p; /* wild pointer */
int a = 10;
/* p is not a wild pointer now*/
p = &a;
/* This is fine. Value of a is changed */
*p = 12;
}
If we want a pointer to a value (or set of values) without having a variable for the
value, we should explicitly allocate memory and put the value in the allocated
memory.
Example
int main()
{
int* p = (int*)malloc(sizeof(int));
// This is fine (assuming malloc doesn't return
// NULL)
*p = 12;
}
8. Constant Pointers
In constant pointers, the memory address stored inside the pointer is constant and
cannot be modified once it is defined. It will always point to the same memory address.
Syntax
data_type * const pointer_name;
9. Pointer to Constant
The pointers pointing to a constant value that cannot be modified are called pointers to
a constant. Here we can only access the data pointed by the pointer, but cannot modify
it. Although, we can change the address stored in the pointer to constant.
Syntax
const data_type * pointer_name;
A pointer pointing to a memory location that has been deleted (or freed) is called a
dangling pointer. Such a situation can lead to unexpected behavior in the program and
also serve as a source of bugs in C programs.
There are three different ways where a pointer acts as a dangling pointer:
1. De-allocation of Memory
int main()
{
int* ptr = (int*)malloc(sizeof(int));
return 0;
}
Output
Memory freed
2. Function Call
When the local variable is not static and the function returns a pointer to that local
variable. The pointer pointing to the local variable becomes dangling pointer.
Example
The below example demonstrates a dangling pointer when the local variable is not
static.
int* fun()
{
// x is local variable and goes out of
// scope after an execution of fun() is
// over.
int x = 5;
return &x;
}
// Driver Code
int main()
{
int* p = fun();
fflush(stdin);
int* fun()
{
// x now has scope throughout the program
static int x = 5;
return &x;
}
int main()
{
int* p = fun();
fflush(stdin);
When a variable goes out of scope the pointer pointing to that variable becomes a
dangling pointer.
Example
// driver code
int main()
{
int* ptr;
// creating a block
{
int a = 10;
ptr = &a;
}
return 0;
}
Output
2355224
# important
The size of the pointers in C is equal for every pointer type. The size of the pointer does
not depend on the type it is pointing to. It only depends on the operating system and
CPU architecture. The size of pointers in C is
• 8 bytes for a 64-bit System
• 4 bytes for a 32-bit System
The reason for the same size is that the pointers store the memory addresses, no
matter what type they are. As the space required to store the addresses of the
different memory locations is the same, the memory required by one pointer type will
be equal to the memory required by other pointer types.
The type declaration is needed in the pointer for dereferencing and pointer
arithmetic purposes.