0% found this document useful (0 votes)
84 views33 pages

C Programming Unit 4.1 Pointers

Pointers are variables that store memory addresses. They allow accessing and modifying the value of the variable located at the stored address. Pointers must be declared with a data type that matches the type of variable being pointed to. A pointer variable is initialized by using the address-of operator (&) to store the address of another variable. The value at the address can then be accessed using the dereference operator (*). Pointers are useful for passing arguments by reference, accessing array elements, dynamic memory allocation, and implementing data structures.

Uploaded by

rishika
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
84 views33 pages

C Programming Unit 4.1 Pointers

Pointers are variables that store memory addresses. They allow accessing and modifying the value of the variable located at the stored address. Pointers must be declared with a data type that matches the type of variable being pointed to. A pointer variable is initialized by using the address-of operator (&) to store the address of another variable. The value at the address can then be accessed using the dereference operator (*). Pointers are useful for passing arguments by reference, accessing array elements, dynamic memory allocation, and implementing data structures.

Uploaded by

rishika
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 33

UNIT 4 (Pointers)

In C, the pointer is a variable used for storing the address of a variable. The variable can be of
any type such as int, char, array, etc.

A pointer is a variable whose value is the address of another variable of the same type. The
variable's value that the pointer points to is accessed by dereferencing using the * operator.
There are different types of pointers such as null, void, wild, etc.

What are Pointers in C?


Every variable we define in our program is stored at a specific location in the memory.
Let's say we define the following integer:
int n = 50;
In our computer’s memory, there are now 4 bytes somewhere that have the binary value
of 50, with some value for its address, like 0x123. Here, 0x123 is a hexadecimal number. As
memory address tends to be large numbers, we often use a hexadecimal number system to
represent them.
We can access this address in our C program using the & operator.

Let's look at an example:


#include<stdio.h>
int main()
{
int n = 50;
printf("Value of the variable n is: %d\n", n);
printf("Memory address of the variable n is: %x\n", &n);
return 0;
}
Output:
Value of the variable n is: 50
Memory address of the variable n is: dcbc14ac
We got dcbc14ac as the address of variable n.
What's the role of a pointer in all this?
A pointer is a variable that stores an address in memory, where some other variable might be
stored.

Syntax of Pointers in C
Just like any other variable or constant, you must declare a pointer before using it to store any
variable address. The general form of a pointer variable declaration is –

type *var-name;
Here, type is the pointer's type; it must be a valid C data type and var-name is the name of the
pointer variable. The asterisk * used to declare a pointer is the same asterisk used for
multiplication. However, in this statement the asterisk is being used to designate a variable as
a pointer.
Take a look at some of the valid pointer declarations −
int *ip; /* pointer to an integer */
double *dp; /* pointer to a double */
float *fp; /* pointer to a float */
char *ch /* pointer to a character */
The actual data type of the value of all pointers, whether integer, float, character, or otherwise,
is the same, a long hexadecimal number that represents a memory address. The only difference
between pointers of different data types is the data type of the variable or constant that the
pointer points to.

How to Use Pointers in C?


• Declare a pointer variable.
• A variable's address is assigned to a pointer using the & operator.
• Use the address in the pointer variable to get the value by using
the *(asterisk) operator, which returns the variable's value at the address indicated by
its argument.

In the above example, a variable int i = 4 is declared, the address of variable i is 0x7766.
A pointer variable int *ptr=&i is declared. It contains the address of variable int i. The
value of *ptr will be value at address 0x7766; that value would be 4.

How to declare a pointer in C?


Syntax:
datatype *pointer_variableName;

Example:
int *ptr1;

Explanation:
For pointer declaration in C, you must make sure that the data type you're using is a valid C
data type and that the pointer and the variable to which the pointer variable points must have
the same data type.

For example, if you want a pointer to point to a variable of data type int, i.e. int var=5 then
the pointer must also be of datatype 'int', i.e. int *ptr1=&var. The * symbol indicates that the
variable is a pointer. To declare a variable as a pointer, you must prefix it with *.
In the example above, we have done a pointer declaration and named ptr1 with the data type
integer.

How to initialize a pointer in C?


There are 2 ways of initializing a pointer in C once the pointer declaration is done. Look at
the example below to understand.

Example:
int a = 5;
int *ptr1 = &a; //Method 1
int *ptr2 = ptr1; //Method 2

Method 1
We make use of the reference operator, i.e. '&' to get the memory address of a variable. It is
also known as the address-of-operator. Look at the figure below to understand what happens:

Method 2
Let us consider the case when we want another pointer to point to the same variable, then, in
that case, we can make use of this method to do the same instead of doing method 1 all over
again i.e. we simply assign the old pointer to the new pointer. Look at the figure below to
understand what happens:
Explanation:
& is a reference operator, which means it is used to get the memory location of the variable.
So 'a' is an integer variable and by doing &a, we are getting the location where a is stored in
the memory and then letting the pointer point to that location. This is the first method of
initializing a pointer in C.

The second method is to initialize a pointer and assign the old pointer to this new pointer.

How to Access a Pointer in C?


You can access both the address in memory where the pointer points to and the value it points
to as well. To do this, let us first understand what a dereference operator is i.e. '*'.

The process of getting a value from a memory address pointed by a pointer is known as
dereferencing. To get the value pointed by a memory address, we utilize the unary
operator, *.

Let us now see an example:


int a=5;
int *ptr1=&a; //Declaring and Initialising the pointer
printf("%p\n",ptr1); //Prints the memory address that the pointer points to
printf("%d",*ptr1); //Prints the value the pointer points to

Output
1010
5

See the image below to understand this further:

Note: When printf("%p\n",ptr1); is called the output is 10101010 as that is the memory
address stored by the pointer ptr1.
Explanation:
Simply printing ptr1 gets you the location in memory that the pointer points to, but to get the
value it points to, you need to make use of a dereference operator(*). The process of getting a
value from a memory address pointed by a pointer is known as dereferencing.

C program to Create, Initialize and Access a Pointer


Let us combine everything that we have learnt and write a combined code for declaring,
initializing and accessing a pointer.
#include <stdio.h>
int main() {
// create int variable
int a=5;

// Initialize the pointer to int variable


int *ptr1;

// Initializing the pointer variable with address of variable


ptr1 = &a;

// accessing value and address of variable a


printf("The Value of a: %d\n", a);
printf("The Address of a: %p\n", &a);

// accessing value and address of ptr1 using dereference operator *


printf("The Value of ptr1: %d\n", *ptr1);
printf("The Address of ptr1: %p", ptr1);
return 0;
}

Output:
The Value of a: 5
The Address of a: 0x7ffd75fe33dc
The Value of ptr1: 5
The Address of ptr1: 0x7ffd75fe33dc

Look at the figure below to get a detailed explanation of the above output:
Example of Pointers in C
Illustration of pointers in C using following code:
#include <stdio.h>

int main()
{
int x = 42; //variable declaration
int *ptr; //pointer variable declaration
ptr = &x; //store address of variable x in pointer ptr
//printing the address
printf("Address stored in a variable ptr is: %x \n", ptr);
//printing the value
printf("Value stored in a variable ptr is: %d \n", *ptr);
return 0;
}

Output:
Address stored in a variable ptr is: a7a9b45c
Value stored in a variable ptr is: 42

Explanation

• In the above example, an int variable x is declared first.


• The memory location where variable x is declared is a7a9b45c. The value stored
in x is 42.
• The pointer variable ptr is declared using *(asterisk) symbol, as mentioned that the
data type of the pointer will also be the same as the variable it will point to.
• In this, ptr = &x, by using & operator, the address of x variable is stored in ptr.
• The value stored in x is accessed using * operator, *ptr will give the value at
location a7a9b45c, i.e., 42.

Features of Pointers:
1. Pointers save memory space.
2. Execution time with pointers is faster because data are manipulated with the
address, that is, direct access to memory location.
3. Memory is accessed efficiently with the pointers. The pointer assigns and releases
the memory as well. Hence it can be said the Memory of pointers is dynamically
allocated.
4. Pointers are used with data structures. They are useful for representing two -
dimensional and multi-dimensional arrays.
5. An array, of any type, can be accessed with the help of pointers, without considering
its subscript range.
6. Pointers are used for file handling.
7. Pointers are used to allocate memory dynamically.
8. In C++, a pointer declared to a base class could access the object of a derived class.
However, a pointer to a derived class cannot access the object of a base class.

Drawbacks of Pointers:
1. If pointers are pointed to some incorrect location then it may end up reading a
wrong value.
2. Erroneous input always leads to an erroneous output
3. Segmentation fault can occur due to uninitialized pointer.
4. Pointers are slower than normal variable
5. It requires one additional dereferences step
6. If we forgot to deallocate a memory then it will lead to a memory leak.

Uses of pointers:
1. To pass arguments by reference
2. For accessing array elements
3. To return multiple values
4. Dynamic memory allocation
5. To implement data structures
6. To do system-level programming where memory addresses are useful

Type of Pointers in C
1. NULL Pointer
2. Void Pointer
3. Wild Pointer
4. Dangling Pointer

What is Null Pointer in C?

• In the C programming language, a null pointer is a pointer that does not point to any
memory location and hence does not hold the address of any variables.
• Null indicates that the pointer is pointing to the first memory location means 0th location.
Normally, the null pointer does not point to anything.
• NULL is a macro constant defined in several header files in the C programming
language,including stdio.h, alloc.h, mem.h, stddef.h, and stdlib.h. Also, take note that
NULL should only be used when working with pointers.

Un-initialized Pointer Problems:


Un-initialized pointer operations are also a big reason for crashes in C and C++ programs.
This problem is so acute that languages like Java and C# do not permit pointer operations.
If a pointer is not initialized before access, this can result in corrupting pretty much any
area of the memory. Sometimes this can result in hard to detect crashes as the pointer
causing memory corruption might be located in completely unrelated area of the code. Also,
un-initialized pointers can lead to unexpected behavior when the memory map of the
application is modified. This happens if an un-initialized pointer operation was corrupting
a unused memory block. Shifting the memory map or resizing of data structures might
cause the corrupting pointer access to modify used memory. This type of problems should
be suspected when a developer has just changed the size of some data structure and a stable
application starts crashing.

To avoid programming errors, use a NULL pointer as a best practice.


1. Before using a pointer, make it a practice to assign it a value. Don't utilize the pointer
before it's been initialized.
2. If you don't have a valid memory location to store in a pointer variable, set it to NULL
instead.
3. Check for a NULL value before utilizing a pointer in any of your function code.

Generally, we can say that a null pointer is a pointer that does not point to any object.

The syntax of a null pointer can be defined in two ways.

int *pointer_var = NULL;


Or

int *pointer_var = 0

Applications of Null Pointer in C

A Null pointer can be used in the following ways:


• When the pointer variable does not point to a valid memory address, it is used
to initialize it.
• Before dereferencing pointers, it is utilized to execute error handling.
• When we don't want to pass the memory location directly, we pass it as a function
argument, which is then utilized to return from a function.

Example of Null Pointer in C

We can assign 0 directly to the pointer so that it will not point to any of the memory locations.

#include <stdio.h>
int main(void) {
int num = 10;
int *ptr1 = &num;
int *ptr2;
int *ptr3=0;

if(ptr1 == 0)
printf("ptr1: NULL\n");
else
printf("ptr1: NOT NULL\n");

if(ptr2 == 0)
printf("ptr2: NULL\n");
else
printf("ptr2: NOT NULL\n");

if(ptr3 == 0)
printf("ptr3: NULL\n");
else
printf("ptr3: NOT NULL\n");
return 0;
}
Output
ptr1: NOT NULL
ptr2: NOT NULL
ptr3: NULL

Explanation: In the above example, we initialized three pointers as *ptr1, *ptr2, and *ptr3,
and we assigned a value to the num variable in *ptr1 and compared it by 0 because *ptr1 is
not equal to null, therefore it would output the result as NOT NULL, and we did not assign any
value to *ptr2 and As a result, the output will be printed as NOT NULL. We assigned value 0
to *ptr3, which is equal to null, hence the output will be NULL.

What are the Uses of NULL Pointer in C?


1. Avoid Crashing a Program
2. While Freeing (de-allocating) Memory
3. NULL Pointer Uses in Linked-List
4. When We Use the malloc() Function

Avoid Crashing a Program


Our program may crash if we pass any garbage value in our code or to a certain method. We can
avoid this by using a NULL pointer. To avoid crasing in a program we can write the code like
this:
#include <stdio.h>
int fun1(int *ptrvarB)
{
if (ptrvarB == NULL)
{
// Handle NULL pointer input
printf("It is null pointer");
}
else
{
printf("It is not a null pointer");
}
}
int main()
{
int *ptrvarA = NULL;
fun1(ptrvarA);
}

Output

It is null pointer

Explanation: We define function fun1() in the following code and pass a pointer ptrvarA to
it. When the function fun1() is called, it checks whether the passed pointer is a null pointer or
not. So we must verify if the pointer's passed value is null or not, because if it is not set to any
value, it will take the garbage value and terminate your program, resulting in the
program crashing.

While Freeing (de-allocating) Memory


Assume you have a pointer pointing to a memory location where data is stored. If you don't
need the data any longer, you should remove it and free up some memory. However, even
after the data has been freed, the pointer still points to the same memory location. A dangling
pointer is a name given to this type of pointer. Set the pointer to NULL to get rid of
the dangling pointer.
#include <stdlib.h>
#include <stdio.h>
int main()
{
int *ptr = (int *)malloc(sizeof(int));

// After below free call, ptr becomes a dangling pointer


free(ptr);

// No more a dangling pointer


ptr = NULL;
}

NULL Pointer Uses in Linked-List


The Data and Link fields are contained in every node. The data field holds the actual data that
we intend to save in the linked list. The link connects each node to the other nodes. The head
of the Linked List has a pointer link to the first node. Because it does not point to another node
in the Linked List, the last node-link has a null pointer. This null variable is updated with the
pointer of the new node when we add a new node at the end of the Linked List.

In Linked List, a NULL pointer is also significant. We know that in a Linked List, we use a
pointer to connect one node to its successor node.

Because there is no successor node for the last node, the link of the last node must be set to
NULL.

***** No need to study this example in detail ******


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

int main()
{
//node structure
struct node
{
int data;
struct node *next;
};

//declaring nodes
struct node *head,*middle,*last;

//allocating memory for each node


head = malloc(sizeof(struct node));
middle = malloc(sizeof(struct node));
last = malloc(sizeof(struct node));

//assigning values to each node


head->data = 10;
middle->data = 20;
last->data = 30;

//connecting each nodes. head->middle->last


head->next = middle;
middle->next = last;
last->next = NULL;

//temp is a reference for head pointer.


struct node *temp = head;

//till the node becomes null, printing each nodes data


while(temp != NULL)
{
printf("%d->",temp->data);
temp = temp->next;
}
printf("NULL");

return 0;
}

Output

10->20->30->NULL

When We Use the malloc() Function

The built-in malloc() method is used to allocate memory in the example below. If the malloc()
function fails to allocate memory, the result is a NULL pointer. As an outcome, a condition to
check whether the value of a pointer is null or not must be included; if the value of a pointer is
not null, memory must be allocated.
#include<stdio.h>
#include<stdlib.h>
int main()
{
int *ptr;
ptr=(int*)malloc(4*sizeof(int));
if(ptr==NULL)
{
printf("Memory is not allocated");
}
else
{
printf("Memory is allocated");
}
return 0;
}

Output:
Memory is allocated

It's usually good programming practice to set a Null value to the pointer when we don't know
the actual memory address.

Examples to Implement Null Pointer in C


Example - 1:
#include<stdio.h>
int main ()
{
int x;
int *ptr = NULL;
if (ptr == 0)
{
ptr = &x;
x = 100;
}
printf ("value of *ptr: %d ", *ptr);
}

Output

value of *ptr: 100


Example - 2:
#include<stdio.h>
int main ()
{
int x, y;
int *ptr = NULL;
if (ptr == 0)
{
ptr = &x;
x = 10;
printf ("value of x = %d", *ptr);
ptr = 0;
}
if (ptr == NULL)
{
ptr = &y;
y = 20;
printf ("\nvalue of y = %d", *ptr);
}
return 0;
}

Output:
value of x = 10
value of y = 20

Example - 3:
#include<stdio.h>
int main ()
{
int x, y;
int *ptr = NULL;
ptr = &x;
x = 32767;
y = *ptr;
printf ("%d %d %d", x, y, *ptr);
*ptr = 0;
printf ("\n%d %d %d", x, y, *ptr);
return 0;
}

Output:
32767 32767 32767
0 32767 0

Difference between NULL and Void Pointer


Void Pointer NULL Pointer
The value that is assigned to the pointer is
In C, the void is a data type.
NULL.
A void pointer is a pointer that has no data type The term "null pointer" refers to a pointer
associated with it. that does not point to anything.
A general-purpose pointer is another name for it. Set a pointer's address to null if no address
It can store any data type's address. has been allocated to it

What is Void Pointer in C?


We all know that pointers in the C language are a special kind of variable that stores the
addresses of other variables, which means to store the address of an int variable, we will be
needing an int pointer, to store address of a float we will be needing a float pointer, to store the
address of a char variable, we will be needing a char pointer and so on. That means the
addresses stored in the pointer variable must be of the same type as specified with the pointer
at the time of pointer declaration Meaning if we declare an int pointer then this pointer cannot
point to the address of floating-point or char variables, it can store the address of an integer
variable only.

The concept of void pointer here is, we declare a pointer to point to void, meaning a generic
pointer, a pointer that has no data type associated with its own, which can point to any data
type and can store any datatype's address as will be required.

In simple words, We can assign the address of any data type to a void pointer, and can later
mould it as per our requirement

Syntax
void *pointerName; // void keyword followed by name of the pointer

Example of Declaring and Initializing Void Pointer in C

We know that void pointers can store the address of any data type, but not only that, a void
pointer can be assigned to any other type of pointer as well without any explicit typecasting.

Examples:
int num = 9; // int variable
int *ptrInt = &num; // int pointer

float num2 = 5.7; // float variable


float *ptrFloat = &num2; // float pointer

// now if we try to do
ptrInt = &num2; // incorrect
ptrInt = ptrFloat; // incorrect
ptrFloat = &num; // incorrect
ptrFloat = ptrInt; // incorrect

// declaring a void pointer


void *ptrVoid;

// initializing a void pointer


ptrVoid = &num; // correct
ptrVoid = ptrInt; // correct
ptrVoid = &num2; // correct
ptrVoid = ptrFloat; // correct

Let's understand it properly with code


#include <stdio.h>
int main()
{
int num = 9; // intializing an int variable
int *ptrInt = &num; // intializing an int pointer

float num2 = 5.7; // initializing a float variable


float *ptrFloat = &num2; // initializing a float pointer
char ch = 'a'; // initialzinng a char variable
char *ptrChar = &ch; // initialzing a char pointer

// declaring a void pointer


void *ptrVoid;

// void pointer storing an address of an int variable


ptrVoid = &num;
printf("%d\n", *(int*)ptrVoid); // typecasting and then printing it

// int pointer assigned to void pointer


ptrVoid = ptrInt;
printf("%d\n", *(int*)ptrVoid); // typecasting and then printing it

// void pointer storing an address of a float variable


ptrVoid = &num2;
printf("%f\n", *(float*)ptrVoid); // typecasting and then printing it

// float pointer assigned to void pointer


ptrVoid = ptrFloat;
printf("%f\n", *(float*)ptrVoid); // typecasting and then printing it

// void pointer storing an address of a char variable


ptrVoid = &ch;
printf("%c\n", *(char*)ptrChar); // typecasting and then printing it

// char pointer assigned to void pointer


ptrVoid = ptrChar;
printf("%c\n", *(char*)ptrChar); // typecasting and then printing it

return 0;
}

Output:

9
9
5.700000
5.700000
a
a

From the above program, we can see that void pointers are not associated with any specific
datatype at the time of their declaration, but we can initialize, and re-assign them to any of
the available data types as per our requirement

Size of the Void Pointer in C


The size of void pointers varies from system to system. For 32-bit systems, the size of a void
pointer is 4 bytes, and if the system is 64-bit, then the size of the void pointer will be 8 bytes.

let's see this with the code

#include <stdio.h>
int main()
{
void *ptr;
printf("The size of the pointer ptr is %d", sizeof(ptr));

return 0;
}

Output: The size of the pointer ptr is 4

The size of any type of pointer is almost the same, as, in the end, they all are just storing the
memory addresses, which is almost constant in the number of bits for any particular system

How does Void Pointer Work in C?

As we have understood till now, a void pointer does not have any associated data type of its
own and is capable of storing the address of any type.

We should also know that void pointers can be typecasted into any type of pointer very easily
and dynamic memory allocation also becomes convenient using void pointers. In C language
functions like malloc(), calloc() etc. are used to dynamically allocate the memory, they return
void pointers, which we can then typecast into any other type of pointer as per our requirement,
and can easily start using them

Now, why typecast?


Because a void pointer cannot be dereferenced, and as we know that to get a value through a
pointer, we firstly need to dereference it using the * operator, but void pointers cannot be
dereferenced, as they are the generic pointers, the compiler in advance will not be able to get
what type of variable is the void pointer pointing to, because there is no presence of a datatype
associated to it at the time of its declaration, meaning compiler will not have any appropriate
source to get an idea of what type of datatype the pointer is pointing to, so dereferencing it
right away will result into an error This is why we first need to typecast a void pointer to the
datatype of our requirement, and after that, we can dereference it and can access its respective
value

Some More Examples of Void Pointer in C


Example 1:
#include <stdio.h>
int main()
{
int num = 9;
int *ptrInt = &num;
void *ptrVoid = &num;
printf("The value of num using num is %d\n", num);
printf("The value of num using ptrInt is %d\n", *ptrInt);
printf("The value of num using ptrVoid is %d\n", *((int*)ptrVoid));
return 0;
}

Output:
The value of num using num is 9
The value of num using ptrInt is 9
The value of num using ptrVoid is 9

Example 2:
#include <stdio.h>
int main()
{
int num = 9;
int *ptrInt = &num;
float num2 = 5.7;
float *ptrFloat = &num2;
void *ptrVoid;
ptrVoid = &num;
printf("The value of num is %d\n", *((int*)ptrVoid));
ptrVoid = &num2;
printf("The value of num is %f\n", *((float*)ptrVoid));
return 0;
}

Output:
The value of num is 9
The value of num is 5.700000

Example 3:
#include <stdio.h>
int main()
{
int intArr[] = {4, 5, 6};
void *ptrVoid = &intArr;
for (int i = 0; i < 3; i++)
{
printf("The value is %d\n", *((int*)ptrVoid));
ptrVoid = ptrVoid + sizeof(int);
}
return 0;
}

Output:
The value is 4
The value is 5
The value is 6

Example 4:
#include <stdio.h>
int main()
{
int num = 9;
float num2 = 5.7;
char ch = 'a';

void *ptrVoid;
ptrVoid = &num;
printf("The value of num is %d\n", *((int*)ptrVoid));

ptrVoid = &num2;
printf("The value of num is %f\n", *((float*)ptrVoid));

ptrVoid = &ch;
printf("The value of num is %c\n", *((char*)ptrVoid));

return 0;
}

Output:
The value of num is 9
The value of num is 5.700000
The value of num is a

Some Important Points Related to Void Pointer

Differencing a Void Pointer in C


As discussed above, we cannot directly difference void pointers, instead, we first need to
typecast it into the appropriate data type then it will be possible to dereference it

Example:
#include <stdio.h>
int main()
{
int num = 9;
void *ptrVoid = &num;
printf("Dereferncing void pointer and the value found is : %d", *ptrVoid); // will give error
return 0;
}

Output:
warning: dereferencing 'void *' pointer
error: invalid use of a void expression

Instead, we should write the above code as


#include <stdio.h>
int main()
{
int num = 9;
void *ptrVoid = &num;
printf("Firstly typecasting the void pointer and then dereferencing it, the value found is:
%d", *((int *)ptrVoid));
return 0;
}

Output:
Firstly typecasting the void pointer and then dereferencing it, the value found is: 9

Arithmetic Operation on Void Pointers

We cannot directly apply arithmetic operations on the void pointers, we need to typecast them
first to apply the pointer arithmetic

Example:
#include <stdio.h>
int main()
{
int intArr[] = {1, 2, 4, 7, 3, 8};
void *ptrVoid = intArr;
for (int i = 0; i < 6; i++)
{
// directly applying pointer arithmetic
printf("%d\n", *(ptrVoid + i));
}
return 0;
}

Output:
warning: dereferencing 'void *' pointer
error: invalid use of a void expression

Instead, we should write the above code as

#include <stdio.h>
int main()
{
int intArr[] = {1, 2, 4, 7, 3, 8};
void *ptrVoid = intArr;
for (int i = 0; i < 6; i++)
{
// typecasting then applying pointer arithmetic
printf("%d\n", *((int *)ptrVoid + i));
}
return 0;
}

Output:
1
2
4
7
3
8

Advantages of Void Pointer


1. There are times, when we are not sure, what type of memory address a pointer variable
will store, then it is best to declare that as a void pointer and later we would typecast it
as per our requirement
2. Another advantage is while dynamic memory allocation functions like malloc(),
and calloc() return a void pointer, which means we can use these functions to allocate
memory dynamically to any data type, whatever the case is, they will be returning only
a void pointer, and we can easily typecast it later as per our need

Uses of Void Pointers in C


1. Void pointers can be used as a generic pointer, to store the address of different data
types at different times
2. Void pointers are helpful in dynamic memory allocations, as functions
like malloc() and calloc() return a void pointer, which can easily be typecasted into any
type of pointer, meaning with the help of void pointers, it is made possible for the
functions like malloc() and calloc() to allocate memory to any datatype without doing
any additional typecasting, because they can simply always written a void pointer,
and then the user can typecast it as per their requirement
Limitations of Using Void Pointers in C
1. We cannot dereference void pointers directly to access the values stored at those
addresses
2. Pointer arithmetic on void pointers is not possible directly, we first need to typecast it
into an appropriate data type, and after that only, we will be able to apply basic
arithmetic operations on it

What is Wild Pointer in C?


The concept of wild pointers is they are almost the same as a normal pointer variable, the
only difference is, that they are an uninitialized pointer variable, meaning they are not
initialized at the time of their declaration, and with being uninitialized, they start to point to
some arbitrary memory location and sometimes may cause the whole program to crash.

In simple words, A wild pointer is a pointer that is not initialized till its first use in the program
hence by default they then hold some arbitrary memory location, and if we try to do any
operation using them, then it can cause our program to crash

ie, a pointer that store neither any valid address in it nor the NULL value, then the pointer is a
wild pointer

Syntax
int *ptr; // wild pointer

We can see that, we just have declared a pointer, but have not given any value to it, so it starts
pointing to some random arbitrary memory location, and hence is referred to as a wild pointer

Example of Wild Pointer in C


#include <stdio.h>
int main()
{
int *ptr; // wild pointer storing some random memory address
// incorrect way of assigning values
*ptr = 9; // this should not be done
printf("The value is %d", *ptr); // trying to print the value
return 0;
}
Output: Undefined Behaviour or Segmentation fault

What we are doing here is, we are declaring an int pointer ptr but not assigning it to any
address, so it becomes a wild pointer, now in the second line, we are trying to access that
arbitrary address which ptr is pointing to and are trying assigning value there, which is an
incorrect way as per the c standards, and then we are trying to print the value, which will result
in unexpected behaviour or segmentation fault or it can cause the program to crash

Note - some compilers may allow the above code to execute and print the value, but this
should be avoided

Instead of this, the above code can be written as


#include <stdio.h>
int main()
{
int *ptr; // wild pointer storing some random memory address
int num = 9; // initialzing an int variable
// valid way of pointer assigning
ptr = &num; // now the pointer ptr stores num's address, ie a meaningful address
printf("The value is %d", *ptr); // dereferencing and printing the value
return 0;
}
Output:
The value is 9

In simple words:
#include <stdio.h>
int main()
{
int num = 9; // line 1
int *ptr; // line 2
ptr = &num; // line 3

return 0;
}

If we only execute line 1 and line 2 in the above code, then ptr will be a wild pointer here, as
we are not storing any value in it, and hence will store an arbitrary memory location, But if we
execute line 3 as well then now ptr will not be a wild pointer anymore, as we are making it
point to a meaningful memory address, and therefore it will become a normal pointer.

Disadvantage of Wild Pointers


1. As when not initialized, a pointer becomes a wild pointer, and starts pointing to some
random memory address, which may lead our program to behave unexpectedly or
sometimes it may crash as well
2. If by mistake we ever tried dereferencing an uninitialized pointer in our program, then
it can result in some unexpected behaviour

Dereferencing a Wild Pointer


As wild pointers point to some random memory location, so dereferencing them is not logical,
as we cannot be sure about the data in the memory our pointer is pointing to ie, what type of
data is stored there, is that data even meaningful or it is some garbage value? This is why it is
advised not to dereference a wild pointer, and if we sometimes try to dereference them, then it
may lead our program to crash or become unresponsive

Conclusion of Wild Pointers


1. Wild Pointers are a special type of pointer, which does not store any meaningful address
nor NULL in them, they point to some arbitrary memory location
2. They are created, when we just declare a pointer and do not initialize it right at the time
of its declaration
3. Syntax : int *ptr; // ptr is now a wild pointer
4. Wild pointers can cause our program to behave unexpectedly and sometimes they might
crash the program
5. To avoid wild pointers, we can either make their point to some valid memory address
or we should make it point to NULL
1. int *ptr = &num; // ptr is storing a valid memory address, assuming num is
already declared
2. int *ptr = (int*)malloc(sizeof(int)); // dynamically allocating a valid memory
address to ptr
3. int *ptr = NULL; // making ptr point to NULL
6. It is advised to avoid dereferencing a wild pointer, doing so can cause our program to
be unresponsive

Dangling Pointer in C
1. The pointers pointing to a deallocated memory block are known as Dangling Pointers.
This condition generates an error known as Dangling Pointer Problem. Dangling
Pointer occurs when a pointer pointing to a variable goes out of scope or when an
object/variable's memory gets deallocated.
2. Also, the occurrence of Dangling Pointers can result in some unexpected errors during
the execution of a program, so we have to make sure to avoid them while writing a
program.
3. There are ways to avoid Dangling Pointer Problems like assigning NULL to the pointer
when the memory gets deallocated.

Introduction to Dangling Pointers in C


In general, Dangling means to hang freely. So as the name suggests, Dangling Pointers are
the pointers that point to some freed/deleted location from the program's memory (memory that
is currently not in the use of the program). When we talk about allocation and deallocation of
memory blocks, we see Dynamic Memory Allocation concepts. In Dynamic Memory
Allocation, generally, we use malloc(), calloc() functions to allocate a memory block
and free() function to deallocate a memory block in C Language. So, once we deallocate a
memory block using the free() function, a Dangling Pointer is generated.

How does Dangling Pointer in C work?


Dangling Pointers are generated when we do not modify the value of a pointer
after deallocation of a memory block or when a variable goes out of scope.

Let us now look at a diagram which represents how a dangling pointer is created. Here, memory
occupied by an integer variable is deallocated, and the pointer pointing to the deallocated
memory acts as a Dangling Pointer (hanging freely).

• An integer pointer ptr points to an integer variable with value 5, ptr contains the address
of the variable.
• When the integer variable gets deallocated from memory, ptr shifts from a regular
pointer to a Dangling Pointer, and it points to some Invalid / Not in use location.

How to avoid Dangling Pointer Errors in C


We have seen three ways where Dangling Pointers can be created.
1. Deallocation of memory blocks performed using free() method.
2. Variable having limited scope during a function call.
3. Variable goes out of scope if the variable is declared inside some inner block of code.

We can avoid these conditions by assigning NULL in case of deallocation of memory and
using static variables in case of variables having local scope.

1. Assigning NULL after De-allocation of memory

We should assign NULL to the ptr pointer as soon as the memory block pointed by the ptr has
been deallocated using the free() function to avoid creating the dangling pointer problem in our
program.

The diagram below shows our algorithm's flow to assign NULL in a pointer as soon as the
memory is deallocated.

1. An integer memory block is allocated using the malloc() function to the ptr pointer, and
then we assign 5 to the memory block pointer by ptr pointer.
2. free(ptr); deallocates the integer memory block pointed by ptr pointer, ptr now points
to some garbage value in the memory.
3. As soon as the memory is deallocated using free(), we assign NULL in the ptr pointer.
It helps to avoid segmentation fault errors and garbage values.

ptr = NULL; ensures that ptr is not a dangling pointer anymore

Other Pointers
The following are some other pointers used in old 16-bit Intel architecture:

Near pointer
A near pointer works with data segments of memory that are in 64Kb of range. It can't access
addresses outside of that data segment. We can make any pointer a near pointer by using the
keyword 'near'. Illustration of Near pointer using following code:

#include<stdio.h>
int main() {
int x = 42;
int near * ptr = & x;
int sz = sizeof(ptr);
printf("size of ptr is %d byte", sz);
return 0;
}

Output
size of ptr is 2 byte

Far pointer

A far pointer has the size of 4 bytes (32 bit), and it can visit memory beyond the current
segment. The compiler allocates a segment register for segment address and another register
for offset within the current segment.

Illustration of far pointer using following code:


#include<stdio.h>
int main() {
int x = 42;
int far * ptr = & x;
int sz = sizeof(ptr);
printf("size of ptr is %d byte", sz);
return 0;
}

Output
size of ptr is 4 byte

Huge Pointer

A huge pointer is similar to a far pointer, the size of a huge pointer is also 4 bytes (32 bit),
and it can also visit memory beyond the current segment.

The main difference between huge and far pointer is of modification of segment. In the far
pointer, the segment part cannot be modified. But in the Huge pointer, the segment part can
be changed.

Pointer Arithmetic in C
Pointers variables are also known as address data types because they are used to store
the address of another variable. The address is the memory location that is assigned to
the variable. It doesn’t store any value.

Hence, there are only a few operations that are allowed to perform on Pointers in C
language. The operations are slightly different from the ones that we generally use for
mathematical calculations. The operations are:
1. Increment/Decrement of a Pointer
2. Addition of integer to a pointer
3. Subtraction of integer to a pointer
4. Subtracting two pointers of the same type
5. Comparison of pointers of the same type.

Increment/Decrement of a Pointer

Increment: It is a condition that also comes under addition. When a pointer is


incremented, it actually increments by the number equal to the size of the data type for
which it is a pointer.

For Example:
If an integer pointer that stores address 1000 is incremented, then it will increment by
4(size of an int) and the new address it will points to 1004. While if a float type
pointer is incremented then it will increment by 4(size of a float) and the new address
will be 1004.

Logic:
Next Location = Current Location + i * size_of(data type)

Example 1: Example for C Pointer Increment.


#include<stdio.h>
void main()
{
int n = 10;
int *a;
a = &n;
printf("current address = %x \n",a);
a = a+1;
printf("next address = %x \n",a);
}

Output:
current address = 5deb5ffc
next address = 5deb6000

Decrement: It is a condition that also comes under subtraction. When a pointer is


decremented, it actually decrements by the number equal to the size of the data type for
which it is a pointer.
For Example:
If an integer pointer that stores address 1000 is decremented, then it will decrement by
4(size of an int) and the new address it will points to 996. While if a float type pointer
is decremented then it will decrement by 4(size of a float) and the new address will
be 996

Logic:
Next Location = Current Location – i * size_of(data type)

Example 2: Example for C Pointer Decrement.


#include<stdio.h>
void main()
{
int n = 10;
int *a;
a = &n;
printf("current address = %x \n",a);
a = a-1;
printf("next address = %x \n",a);
}

Output
current address = d460f58c
next address = d460f588

C Pointer Addition:
Addition in pointer in C simply means to add a value to the pointer value.

Logic:
Next Location = Current Location + (Value To Add * size_of(data type))

Example 3: Example for C Pointer Addition.


#include<stdio.h>

void main()
{
int n = 10;
int *a;
a = &n;
printf("current address = %x \n",a);
a = a + 4;
printf("next address = %x \n",a);
}

Output
current address = bfdcd58c
next address = bfdcd59c

C Pointer Subtraction:
Subtraction from a pointer in C simply means to subtract a value from the pointer value.

Logic:
Next Location = Current Location – (Value To Add * size_of(data type))
Example 4: Example for C Pointer Subtraction.
#include<stdio.h>
void main()
{
int n = 10;
int *a;
a = &n;
printf("current address = %x \n",a);
a = a - 2;
printf("next address = %x \n",a);
}

Output
current address = f2fd491c
next address = f2fd4914

Pointer Arithmetic on Arrays:

Pointers contain addresses. Adding two addresses makes no sense because there is no
idea what it would point to. Subtracting two addresses lets you compute the offset
between the two addresses. An array name acts like a pointer constant. The value of this
pointer constant is the address of the first element. For Example: if an array named arr
then arr and &arr[0] can be used to reference array as a pointer.

Pointer arithmetic is meaningless unless performed on an array.


Below is the program to illustrate the Pointer Arithmetic on arrays:

// C program to illustrate the array traversal using pointers


#include <stdio.h>
int main()
{
int N = 5;
int arr[] = { 1, 2, 3, 4, 5 };
int* ptr;

ptr = arr; // Point the pointer to first element in array arr[]


// Traverse array using ptr
for (int i = 0; i < N; i++) {
printf("%d ", ptr[0]); // Print element at which ptr points
ptr++;
}
}

Output
12345

Relationship between Pointers and Arrays in C


Array and Pointers in C Language hold a very strong relationship. Generally, pointers are the
variables which contain the addresses of some other variables and with arrays a pointer stores
the starting address of the array. Array name itself acts as a poin ter to the first element of the
array and also if a pointer variable stores the base address of an array then we can manipulate
all the array elements using the pointer variable only. Pointers can be associated with the
multidimensional arrays (2-D and 3-D arrays) as well. Also, We can create an array of pointers
to store multiple addresses of different variables.

Let us now look at how an array is stored in our system's memory and how to declare and
initialize an array then, we will move to the relationship of pointers with arrays.

In C Language, we can declare an integer array using the below statement:

int arr[5];

The above statement will allocate 55 integer blocks and will occupy a memory of 20 Bytes in
the system (5 * 4 = 20, 5 is size of the array and 44 bytes is the space occupied by an integer
block, total = 20).

Below is the representation of how the array is stored in the system's memory. Let the base
address allocated by the system to the array is 300.

With respect to the concept of the pointer, let us see some important points related to arrays
in general :

• 'arr' serves two purposes here, first it is the name of the array and second, arr itself
represents the base address of the array i.e. 300300 in the above case, if we print the value
in arr then it will print the address of the first element in the array.
• As the array name arr itself represents the base address of the array, then by
default arr acts as a pointer to the first element of the array.
• arr is the same as &arr and &arr[0] in C Language.
• If we use dereferencing operator (*) on any of the above representations of array address,
we will get the value of the very first element of the array.

Let us look at the program below to see arr, &arr and &arr[0] means the same.

#include <stdio.h>
int main() {
int arr[5] = {3, 5, 7, 9, 11};
printf("arr : %u, Value : %d\n", arr, *arr);
printf("&arr : %u, Value : %d\n", &arr, *(arr));
printf("&arr[0] : %u, Value : %d\n", &arr[0], *( &arr[0]));
return 0;
}
Output:
[Success] Your code was executed successfully
arr : 63744176, Value : 3
&arr : 63744176, Value : 3
&arr[0] : 63744176, Value : 3

Syntax Representing Array in Terms of Pointers in C


In a C Program, we denote array elements as arr[i], where i is the index value. Below is a
similar syntax in terms of pointers of how we can represent the array elements using the
dereferencing operator (*) on the array name i.e. using the pointers property of the array.
*(arr + i)

• * is a dereferencing operator used to extract the value from the address (arr + i).
• *(arr + i) is the same as arr[i] in a C Program.
• arr represents the array name and i represents the index value.

Let us look at a program to print the values and address of the array elements using the
above syntax.

#include <stdio.h>

int main()
{
int arr[5] = {2, 4, 6, 8, 10}, i;

for(i = 0; i < 5; i++)


{
printf("[index %d] Address : %u, Value : %d\n", i, (arr + i), *(arr + i));
}
return 0;
}

Output:
[Success] Your code was executed successfully
[index 0] Address : 2364420656, Value : 2
[index 1] Address : 2364420660, Value : 4
[index 2] Address : 2364420664, Value : 6
[index 3] Address : 2364420668, Value : 8
[index 4] Address : 2364420672, Value : 10

Note : Output address will be different at every run

Pointer to 2D Arrays
A 2-D array is an array of arrays, we can understand 2-D array as they are made up of n 1-D
arrays stored in a linear manner in the memory. 2-D arrays can also be represented in a matrix
form. In the matrix form, there are rows and columns, so let's look at the representation of a
2-D array matrix below where i represents the row number and j represents the column
number, arr is the array name.
Here, array contains 3 1-D arrays as its element, so array name arr acts as a pointer to the
1 st 1-D array i.e. arr[0] and not to the first element of the array i.e. arr[0][0]. As we know our
system's memory is organized in a sequential manner so it is not possible to store a 2 -D array
in rows and columns fashion, they are just used for the logical representation of 2 -D arrays.

In the above representation, we have combined 3 1-D arrays that are stored in the memory to
make a 2-D array, herearr[0],arr[1], arr[2] represents the base address of the respective
arrays. So, arr[0], arr[1] and arr[2] act as a pointer to these arrays and we can access the 2-D
arrays using the above array pointers.

Let us see the syntax of how we can access the 2-D array elements using pointers.

Syntax for representing 2-D array elements :

*(*(arr + i) + j)

Note : *(*(arr + i) + j) represents the element of an array arr at the index value of ith row
and jth column; it is equivalent to the regular representation of 2-D array elements as arr[i][j].

Let us look at an example, here we are initializing and printing the elements of the 2 -D array
using the pointers concept.
#include <stdio.h>
int main()
{
int arr[3][3] = {{2, 4, 6},
{0, 1, 0},
{3, 5, 7}};
int i, j;
printf("Addresses : \n");
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
printf("%u[%d%d] ", (*(arr + i) + j), i, j);
}
printf("\n");
}
printf("Values : \n");
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
printf("%d[%d%d] ", *(*(arr + i) + j), i, j);
}
printf("\n");
}

return 0;
}
OUTPUT :
[Success] Your code was executed successfully
Addresses :
4201367232[00] 4201367236[01] 4201367240[02]
4201367244[10] 4201367248[11] 4201367252[12]
4201367256[20] 4201367260[21] 4201367264[22]
Values :
2[00] 4[01] 6[02]
0[10] 1[11] 0[12]
3[20] 5[21] 7[22]

Note : Output address will be different at every run

What is an Array of Pointers in C?


When we want to point at multiple addresses of the same data type in a C program, we use an
array of pointers. Since a pointer variable always contains an address, so an array of pointers
would be nothing but a collection of addresses.

Declaration of an Array of Pointers in C


An array of pointers can be declared just like we declare the arrays of char, float, int, etc. The
syntax for declaring an array of pointers would be:
data_type *name_of_array [array_size];
Now, let us take a look at an example for the same,
int *ary[55]
This one is an array of a total of 55 pointers. In simple words, this array is capable of holding
the addresses a total of 55 integer variables. Think of it like this- the ary[0] will hold the
address of one integer variable, then the ary[1] will hold the address of the other integer
variable, and so on
Let us take a look at a program that demonstrates how one can use an array of pointers in C:
#include<stdio.h>
#define SIZE 10
int main()
{
int *arr[3];
int p = 40, q = 60, r = 90, i;
arr[0] = &p;
arr[1] = &q;
arr[2] = &r;
for(i = 0; i < 3; i++)
{
printf(“For the Address = %d\t the Value would be = %d\n”, arr[i], *arr[i]);
}
return 0;
}
The output generated out of the program mentioned above would be like this:
For the Address = 387130656 the Value would be = 40
For the Address = 387130660 the Value would be = 60
For the Address = 387130664 the Value would be = 90

How Does it Work?


Take a look at how we assigned the addresses of the pointers p, q, and r. We first assign the p
variable’s address to the 0th element present in the array. In a similar manner, the 1st and 2nd
elements of the array will be assigned with the addresses of q and r. At this very point, the
array, arr would look like this:

The arr[i] provides the address of the array’s ith element. Thus, the arr[0] would return the
address of the variable p, the arr[1] would return the address of the pointer q, and so on. We
use the * indirection operator to get the value present at the address. Thus, it would be like
this:
*arr[i]
Thus, *arr[0] would generate the value at the arr[0] address. In a similar manner, the *arr[1]
would generate the value at the arr[1] address, and so on.

Difference Between Pointer to an Array and Array of Pointers


What is a Pointer to an Array?
The array pointer is an alternative name to a pointer to an array. We generally make use of
this pointer for accessing the various components of any given array. The pointer ptr
basically focuses on the 0th component of any given array. Likewise, one can easily declare a
point that is capable of pointing to a whole array- rather than only a single array component.
What is an Array of Pointers?
It refers to an array of various pointer variables, also called the pointer arrays. One can easily
create separate pointer variables pointing towards different ranges of values. And we can also
create a single integer array of pointers pointing towards all the available values.
Here is a list of the differences present between Pointer to an Array and Array of Pointers.
Parameters Pointer to an Array Array of Pointers

Uses and A user creates a pointer for storing A user creates an array of pointers that
Purposes the address of any given array. basically acts as an array of multiple
pointer variables.

Alternative It is alternatively known as an These are alternatively known as pointer


Names array pointer. arrays.

Allocation One can allocate these during the One can allocate these during the
run time. compile time.

Initialization You cannot initialize a pointer to You can easily initialize an array at the
at Definition the definition. definition level.

Nature It is dynamic in nature. It is static in nature.

Resizing One can easily resize the allocated Once we declare the size of an array, we
memory of a pointer later at any cannot resize it any time we want
given time. according to our requirements.

Type of A typical pointer variable is The size of any given array decides the
Storage capable of storing only a single total number of variables that it can store
variable within. within.

You might also like