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

Data Structures 1st Unit

Uploaded by

12215139
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views33 pages

Data Structures 1st Unit

Uploaded by

12215139
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 33

DATA STRUCTURES

Data may be organized in many different ways logical or mathematical model of a


program particularly organization of data. This organized data is called “Data
Structure”.

Or

The organized collection of data is called a ‘Data Structure’.

Data Structure=Organized data +Allowed operations

Data Structure involves two complementary goals. The first goal is to identify and
develop useful, mathematical entities and operations and to determine what class of
problems can be solved by using these entities and operations. The second goal is
to determine representation for those abstract entities to implement abstract
operations on this concrete representation.

Primitive Data structures are directly supported by the language ie; any operation
is directly performed in these data items. Ex: integer, Character, Real numbers etc.

Non-primitive data types are not defined by the programming language, but are
instead created by the programmer.

Linear data structures organize their data elements in a linear fashion, where data
elements are attached one after the other. Linear data structures are very easy to
implement, since the memory of the computer is also organized in a linear fashion.
Some commonly used linear data structures are arrays, linked lists, stacks and
queues.
In nonlinear data structures, data elements are not organized in a sequential
fashion. Data structures like multidimensional arrays, trees, graphs, tables and sets
are some examples of widely used nonlinear data structures.

Operations on the Data Structures:

Following operations can be performed on the data structures:

1. Traversing

2. Searching

3. Inserting

4. Deleting

5. Sorting

6. Merging

1. Traversing- It is used to access each data item exactly once so that it can be
processed.

2. Searching- It is used to find out the location of the data item if it exists in the given
collection of data items.

3. Inserting- It is used to add a new data item in the given collection of data items.

4. Deleting- It is used to delete an existing data item from the given collection of
data items.

5. Sorting- It is used to arrange the data items in some order i.e. in ascending or
descending order in case of numerical data and in dictionary order in case of
alphanumeric data.

6. Merging- It is used to combine the data items of two sorted files into single file in
the sorted form.

SORTING

Sorting:

Sorting is a technique of organizing the data. It is a process of arranging the records,


either in ascending or descending order i.e. bringing some order lines in the data.
Sort methods are very important in Data structures. Sorting can be performed on any
one or combination of one or more attributes present in each record. It is very easy
and efficient to perform searching, if data is stored in sorting order. The sorting is
performed according to the key value of each record. Depending up on the makeup
of key, records can be stored either numerically or alphanumerically. In numerical
sorting, the records arranged in ascending or descending order according to the
numeric value of the key.

Let A be a list of n elements A1, A2, A3 …………………….An in memory. Sorting A


refers to the operation of rearranging the contents of A so that they are increasing in
order, that is, so that A1 <=A2 <=A3 <=…………….<=An. Since A has n elements,
there are n! Ways that the contents can appear in A. these ways corresponding
precisely to the n! Permutations of 1,2,3,………n. accordingly each sorting algorithm
must take care of these n! Possibilities.

Ex: suppose an array DATA contains 8elements as follows:

DATA: 70, 30,40,10,80,20,60,50.

After sorting DATA must appear in memory as follows:

DATA: 10 20 30 40 50 60 70 80

Since DATA consists of 8 elements, there are 8!=40320 ways that the numbers
10,20,30,40,50,60,70,80 can appear in DATA.

The factors to be considered while choosing sorting techniques are:

 Programming Time
 Execution Time
 Number of Comparisons
 Memory Utilization
 Computational Complexity

Types of Sorting Techniques:

Sorting techniques are categorized into 2 types. They are Internal Sorting and
External Sorting.

Internal Sorting: Internal sorting method is used when small amount of data has to
be sorted. In this method, the data to be sorted is stored in the main memory
(RAM).Internal sorting method can access records randomly. EX: Bubble Sort,
Insertion Sort, Selection Sort, Shell sort, Quick Sort, Radix Sort, Heap Sort etc.

External Sorting: Extern al sorting method is used when large amount of data has
to be sorted. In this method, the data to be sorted is stored in the main memory as
well as in the secondary memory such as disk. External sorting methods an access
records only in a sequential order. Ex: Merge Sort, Multi way Mage Sort.

Complexity of sorting Algorithms: The complexity of sorting algorithm measures


the running time as a function of the number n of items to be stored. Each sorting
algorithm S will be made up of the following operations, where A1, A2, A3
…………………….An contain the items to be sorted and B is an auxiliary location.

SELECTION SORT:

In selection sort, the smallest value among the unsorted elements of the array is
selected in every pass and inserted to its appropriate position into the array. First,
find the smallest element of the array and place it on the first position. Then, find the
second smallest element of the array and place it on the second position. The
process continues until we get the sorted array. The array with n elements is sorted
by using n-1 pass of selection sort algorithm.

 In 1st pass, smallest element of the array is to be found along with its index
pos. then, swap A[0] and A[pos]. Thus A[0] is sorted, we now have n -1
elements which are to be sorted.
 In 2nd pas, position pos of the smallest element present in the sub-array A[n1]
is found. Then, swap, A[1] and A[pos]. Thus A[0] and A[1] are sorted, we now
left with n-2 unsorted elements.
 In n-1th pass, position pos of the smaller element between A[n-1] and A[n-2]
is to be found. Then, swap, A[pos] and A[n-1].

Therefore, by following the above explained process, the elements A[0], A[1], A[2], ...
, A[n-1] are sorted.

Example: Consider the following array with 6 elements. Sort the elements of the
array by using selection sort.

A = {10, 2, 3, 90, 43, 56}


Sorted A = {2, 3, 10, 43, 56, 90}

Algorithm

SELECTION SORT (ARR, N)

Step 1: Repeat Steps 2 and 3 for K = 1 to N-1

Step 2: CALL SMALLEST(A, K, N, POS)

Step 3: SWAP A[K] with

A[POS] [END OF LOOP]

Step 4: EXIT B

BUBBLE SORT

Bubble Sort: This sorting technique is also known as exchange sort, which arranges
values by iterating over the list several times and in each iteration the larger value
gets bubble up to the end of the list. This algorithm uses multiple passes and in each
pass the first and second data items are compared. if the first data item is bigger
than the second, then the two items are swapped. Next the items in second and third
position are compared and if the first one is larger than the second, then they are
swapped, otherwise no change in their order. This process continues for each
successive pair of data items until all items are sorted.

Bubble Sort Algorithm:

Step 1: Repeat Steps 2 and 3 for i=1 to 10


Step 2: Set j=1

Step 3: Repeat while j<=n

(A)

if a[i] < a[j] Then

Interchange a[i] and a[j]

[End of if]

(B)

Set j = j+1

[End of Inner Loop]

[End of Step 1 Outer Loop]

Step 4: Exit
INSERTION SORT

Insertion sort is one of the best sorting techniques. It is twice as fast as Bubble sort.
In Insertion sort the elements comparisons are as less as compared to bubble sort.
In this comparison the value until all prior elements are less than the compared
values is not found. This means that all the previous values are lesser than
compared value. Insertion sort is good choice for small values and for nearly sorted
values.

Working of Insertion sort:

The Insertion sort algorithm selects each element and inserts it at its proper position
in a sub list sorted earlier. In a first pass the elements A1 is compared with A0 and if
A[1] and A[0] are not sorted they are swapped. In the second pass the element[2] is
compared with A[0] and A[1]. And it is inserted at its proper position in the sorted sub
list containing the elements A[0] and A[1]. Similarly doing i th iteration the element
A[i] is placed at its proper position in the sorted sub list, containing the elements
A[0],A[1],A[2],…………A[i-1].

To understand the insertion sort consider the unsorted Array

A={7,33,20,11,6}

The steps to sort the values stored in the array in ascending order using Insertion
sort are given below:

Step 1: The first value i.e; 7 is trivially sorted by itself.

Step 2: the second value 33 is compared with the first value 7. Since 33 is greater
than 7, so no changes are made.

Step 3: Next the third element 20 is compared with its previous element (towards
left).Here 20 is less than 33.but 20 is greater than 7. So it is inserted at second
position. For this 33 is shifted towards right and 20 is placed at its appropriate
position.
Step 4: Then the fourth element 11 is compared with its previous elements. Since 11
is less than 33 and 20; and greater than 7. So it is placed in between 7 and 20. For
this the elements 20 and 33 are shifted one position towards the right.

Step5: Finally the last element 6 is compared with all the elements preceding it.
Since it is smaller than all other elements, so they are shifted one position towards
right and 6 is inserted at the first position in the array. After this pass, the Array is
sorted.

Step 6: Finally the sorted Array is as follows:

ALGORITHM:

Insertion_sort(ARR,SIZE)

Step 1: Set i=1;

Step 2: while(i<SIZE)

Set temp=ARR[i]

J=i=1;

While(Temp<=ARR[j] and j>=0)

Set ARR[j+1]=ARR[i]

Set j=j-1

End While

SET ARR(j+1)=Temp;
Print ARR after ith pass

Set i=i+1

End while

Step 3: print no.of passes i-1

Step 4: end

Advantages of Insertion Sort:

 It is simple sorting algorithm, in which the elements are sorted by considering


one item at a time. The implementation is simple.
 It is efficient for smaller data set and for data set that has been substantially
sorted before.
 It does not change the relative order of elements with equal keys.
 It reduces unnecessary travels through the array.
 It requires constant amount of extra memory space.

Disadvantages:-

 It is less efficient on list containing more number of elements.


 As the number of elements increases the performance of program would be
slow.

Complexity of Insertion Sort:

BEST CASE:- Only one comparison is made in each pass. The Time complexity is
O(n2 ).

WORST CASE:- In the worst case i.e; if the list is arranged in descending order, the
number of comparisons required by the insertion sort is given by:
1+2+3+……………………….+(n-2)+(n-1)= (n*(n-1))/2; = (n2 -n)/2. The number of
Comparisons are O(n2 ).

AVERAGE CASE:- In average case the numer of comparisons is given by 1 2 + 2 2


+ 3 3 + ⋯ + (n−2) 2 + (n−1) 2 = n∗(n−1) 2∗2 =(n2 -n)/4 = O(n2 ).

QUICK SORT

The Quick Sort algorithm follows the principal of divide and Conquer. It first picks up
the partition element called ‘Pivot’, which divides the list into two sub lists such that
all the elements in the left sub list are smaller than pivot and all the elements in the
right sub list are greater than the pivot. The same process is applied on the left and
right sub lists separately. This process is repeated recursively until each sub list
containing more than one element.
Working of Quick Sort:

The main task in Quick Sort is to find the pivot that partitions the given list into two
halves, so that the pivot is placed at its appropriate position in the array. The choice
of pivot as a significant effect on the efficiency of Quick Sort algorithm.

Pivot Selection: The choice of the pivot element can affect the performance of the
algorithm. Common strategies include:

 First element: Pick the first element of the array.


 Last element: Pick the last element of the array.
 Random element: Pick a random element as the pivot.
 Median-of-three: Pick the median of the first, middle, and last elements.

Partitioning: This step rearranges the array such that elements less than the pivot
come before it and elements greater than the pivot come after it. The pivot is placed
in its correct position.

Recursion: The algorithm recursively applies the same process to the sub-arrays
formed by the partitioning step.

Steps of Quick Sort

1. Choose a Pivot: Select an element from the array to serve as the pivot.
2. Partitioning: Rearrange the elements in the array so that elements less than
the pivot come before it, and elements greater than the pivot come after it.
The pivot is then placed in its correct sorted position.
3. Recursively Apply: Apply the same process to the sub-arrays on either side
of the pivot.

Pseudocode for Quick Sort

QUICK_SORT(array, low, high)

if low < high

pivot_index = PARTITION(array, low, high)

QUICK_SORT(array, low, pivot_index - 1)

QUICK_SORT(array, pivot_index + 1, high)

PARTITION(array, low, high)

pivot = array[high] // Choose the last element as pivot

i = low - 1 // Index of smaller element


for j from low to high - 1

if array[j] <= pivot

i=i+1

swap(array[i], array[j])

swap(array[i + 1], array[high])

return i + 1

Explanation of Pseudocode

1. QUICK_SORT:

 Takes array, low, and high as parameters.


 Recursively sorts the sub-arrays.
 Calls PARTITION to rearrange the elements around the pivot.

2. PARTITION:

 Chooses the last element as the pivot.


 Rearranges elements so that elements smaller than the pivot are on the left,
and elements larger are on the right.
 Moves the pivot into its correct position and returns its index.

Example

Let’s go through an example to sort the array [3, 6, 8, 10, 1, 2, 1] using Quick Sort.

Initial Array: [3, 6, 8, 10, 1, 2, 1]

Step 1: Call QUICK_SORT(array, 0, 6)

Choose pivot as 1 (last element).

PARTITION:

 i starts at -1

Traverse elements:

 3 > 1 (no swap)


 6 > 1 (no swap)
 8 > 1 (no swap)
 10 > 1 (no swap)
 <= 1 → swap array[0] and array[4], array becomes [1, 6, 8, 10, 3, 2, 1], i
becomes 0
 2 <= 1 → swap array[1] and array[5], array becomes [1, 1, 8, 10, 3, 2, 6], i
becomes 1
 Swap pivot 1 with array[2], array becomes [1, 1, 2, 10, 3, 8, 6]
 Pivot index is 2

Recursively sort sub-arrays:

 QUICK_SORT(array, 0, 1):
 Pivot is 1
 Partition is already sorted (base case of recursion).

o QUICK_SORT(array, 3, 6):

 Pivot is 6
 PARTITION:
 i starts at 2
 Traverse elements:
 10 > 6 (no swap)
 3 < 6 → swap array[3] and array[4], array
becomes [1, 1, 2, 3, 10, 8, 6], i becomes 3
 8 > 6 (no swap)
 Swap pivot 6 with array[4], array becomes [1, 1, 2,
3, 6, 8, 10]
 Pivot index is 4
 Recursively sort sub-arrays:
 QUICK_SORT(array, 3, 3) (base case, already
sorted).
 QUICK_SORT(array, 5, 6):
 Pivot is 10
 PARTITION:
 i starts at 4
 Traverse elements:
 8 < 10 → swap array[5] and
array[5] (no change needed), i
becomes 5
 Swap pivot 10 with array[6], array
remains [1, 1, 2, 3, 6, 8, 10]
 Pivot index is 6
 Recursively sort sub-arrays:
 QUICK_SORT(array, 5, 5) (base
case, already sorted).

Final Sorted Array: [1, 1, 2, 3, 6, 8, 10]

Advantages of Quick Sort:

 This is fastest sorting technique among all.


 It efficiency is also relatively good.
 It requires small amount of memory.
Disadvantages:

 It is somewhat complex method for sorting.


 It is little hard to implement than other sorting methods
 It does not perform well in the case of small group of elements.

Complexities of Quick Sort:

Average Case: The running time complexity is O(n log n).

Worst Case: Input array is not evenly divided. So the running time complexity is
O(n2 ).

Best Case: Input array is evenly divided. So the running time complexity is O(n
logn).

Merge Sort

Merge Sort is an efficient, stable, and comparison-based sorting algorithm that


follows the divide-and-conquer approach. It divides the input array into two halves,
recursively sorts them, and finally merges the two sorted halves.

Key Steps in Merge Sort:

1. Divide: Split the array into two halves.

2. Conquer: Recursively sort each half.

3. Merge: Combine the two sorted halves to produce the sorted array.

Example:

Consider an array A = [38, 27, 43, 3, 9, 82, 10]. The merge sort process will proceed
as follows:

1. Divide:

o First split: [38, 27, 43] and [3, 9, 82, 10]

o Further splits:

 [38, 27] and [43]

 [38] and [27]

 [3, 9] and [82, 10]

 [3] and [9], [82] and [10]

2. Conquer:

o Sort each small part:


 [27, 38] and [43] → [27, 38, 43]

 [3, 9] and [10, 82] → [3, 9, 10, 82]

3. Merge:

o Merge the sorted arrays:

 [27, 38, 43] and [3, 9, 10, 82] merge to form [3, 9, 10, 27, 38, 43,
82].

Merge Sort Algorithm:

 Time Complexity: O(n log n) in all cases (best, average, and worst).

 Space Complexity: O(n), as it requires extra space proportional to the size of


the input array for the merging process.

 Stability: Merge sort is a stable sort, meaning that it maintains the relative
order of equal elements.

Pseudocode:

MERGE_SORT(array, left, right)

if left < right

middle = (left + right) / 2

MERGE_SORT(array, left, middle)

MERGE_SORT(array, middle + 1, right)

MERGE(array, left, middle, right)

MERGE(array, left, middle, right)

n1 = middle - left + 1

n2 = right - middle

LeftArray[n1], RightArray[n2]

for i = 0 to n1 - 1

LeftArray[i] = array[left + i]

for j = 0 to n2 - 1
RightArray[j] = array[middle + 1 + j]

i = 0, j = 0, k = left

while i < n1 and j < n2

if LeftArray[i] <= RightArray[j]

array[k] = LeftArray[i]

i=i+1

else

array[k] = RightArray[j]

j=j+1

k=k+1

while i < n1

array[k] = LeftArray[i]

i=i+1

k=k+1

while j < n2

array[k] = RightArray[j]

j=j+1

k=k+1

Advantages of Merge Sort:

 Predictable Performance: O(n log n) time complexity ensures efficient


sorting even for large datasets.

 Stable Sorting: Preserves the relative order of equal elements, which is


crucial in many applications.

 Versatile: Works well for sorting linked lists and external sorting (sorting data
that doesn't fit into memory).
Disadvantages:

 Space Complexity: Requires additional memory proportional to the size of


the input array.

 Complex Implementation: Although conceptually simple, merge sort can be


more complex to implement correctly compared to simpler algorithms like
insertion or bubble sort.

Merge Sort is particularly useful in scenarios where a stable sort is required, or when
working with large datasets that need consistent performance regardless of input
order.

Searching
What is searching in Data Structures?
Searching is the fundamental process of locating a specific element or item within a
collection of data. This collection of data can be arrays, lists, trees, or other
structured representations. Data structures are complex systems designed to
organize vast amounts of information. Searching within a data structure is one of the
most critical operations performed on stored data.

The goal is to find the desired information with its precise location quickly and with
minimal computational resources. It plays an important role in various computational
tasks and real-world applications, including information retrieval, data analysis,
decision-making processes, etc.

Characteristics of Searching

 Target Element/Key: It is the element or item that you want to find within the
data collection. This target could be a value, a record, a key, or any other data
entity of interest.
 Search Space: It refers to the entire collection of data within which you are
looking for the target element. Depending on the data structure used, the
search space may vary in size and organization.
 Complexity: Searching can have different levels of complexity depending on
the data structure and the algorithm used. The complexity is often measured
in terms of time and space requirements.
 Deterministic vs. Non-deterministic: The algorithms that follow a clear,
systematic approach, like binary search, are deterministic. Others, such as
linear search, are non-deterministic, as they may need to examine the entire
search space in the worst case.

Searching Techniques: Linear Search and Binary Search

Linear Search
1. Start at the beginning of the list.
2. Compare the target value with the current element in the list.
3. If the current element matches the target value, the search is successful, and
the position or index of the element is returned.
4. If the current element does not match the target value, move to the next
element in the list.
5. Repeat steps 2-4 until a match is found or the end of the list is reached.
6. If the end of the list is reached without finding a match, the search is
unsuccessful, and a special value (e.g., -1) may be returned to indicate the
absence of the target value

Pseudocode for Linear Search Algorithm

LinearSearch(array, target):
for each element in array, from left to right:
if element equals target:
return index of element
return -1 // target not found in the array

Example of Linear Search

 Python
 Java
 C++

num = 5

arr = [10, 20, 30, 40, 50]

search = 30

found = False

for cnt in range(num):


if arr[cnt] == search:
print(f"{search} is present at location {cnt + 1}.")
found = True
break

if not found:
print(f"{search} is not present in the array.")

Run Code >>

Output

30 is present at location 3.

Binary Search
1. Start with a sorted array or list. For binary search to work correctly, the
elements must be in ascending or descending order.
2. Set two pointers, low and high, to the beginning and end of the search space,
respectively. Initially, low = 0 and high = length of the array - 1.
3. Calculate the middle index using the formula: mid = (low + high) / 2. This will
give you the index of the element in the middle of the current search space.
4. Compare the target value with the element at the middle index:
o If they are equal, the target value has been found. Return the index of
the middle element.
o If the target value is less than the middle element, set high = mid - 1
and go to step 3.
o If the target value is greater than the middle element, set low = mid + 1
and go to step 3.
5. Repeat steps 3-4 until the target value is found or low > high. If low becomes
greater than high, it means the target value is not present in the array.

Pseudocode for Binary Search Algorithm

BinarySearch(array, target): left = 0 // Initialize the left pointer


right = Length(array) - 1 // Initialize the right pointer while left <= right:
mid = (left + right) / 2 // Calculate the middle index if array[mid] == target:
return mid // Target found at the middle index
else if array[mid] < target:
left = mid + 1 // Adjust left pointer
else:
right = mid - 1 // Adjust right pointer return -1 // Target not found in the array

Example of Binary Search

 Python
 Java
 C++

size = 6
lst = [1, 3, 5, 4, 10, 7]
sElement = 10

f=0
l = size - 1
m = (f + l) // 2

while f <= l:
if lst[m] < sElement:
f=m+1
elif lst[m] == sElement:
print("Element found at index", m, ".")
break
else:
l=m-1
m = (f + l) // 2

if f > l:
print("Element not found in the list.")

Run Code >>

Output

Element found at index 4.

Revision of Pointers:

Pointers are one of the core components of the C programming language. A


pointer can be used to store the memory address of other variables, functions, or
even other pointers. The use of pointers allows low-level memory access, dynamic
memory allocation, and many other functionality in C.

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.

How to Use Pointers?


The use of pointers in C can be divided into three steps:
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
operator that we used in the pointer declaration.

Dereferencing a Pointer in C

C Pointer Example
C
// C program to illustrate Pointers
#include <stdio.h>

void geeks()
{
int var = 10;

// declare pointer variable


int* ptr;

// note that data type of ptr and var must be same


ptr = &var;

// assign the address of a variable to a pointer


printf("Value at ptr = %p \n", ptr);
printf("Value at var = %d \n", var);
printf("Value at *ptr = %d \n", *ptr);
}

// Driver program
int main()
{
geeks();
return 0;
}

Output
Value at ptr = 0x7ffca84068dc
Value at var = 10
Value at *ptr = 10

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;

These pointers are pronounced as Pointer to Integer.


Similarly, a pointer can point to any primitive data type. It can point also point to
derived data types such as arrays and user-defined data types such as structures.

2. Array Pointer
Pointers and Array are closely related to each other. Even the array name is the
pointer to its first element. They are also known as Pointer to Arrays. We can
create a pointer to an array using the given syntax.
Syntax
char *ptr = &array_name;

Pointer to Arrays exhibits some interesting properties which we discussed later in


this article.

3. Structure Pointer
The pointer pointing to the structure type is called Structure Pointer or Pointer to
Structure. It can be declared in the same way as we declare the other primitive
data types.
Syntax
struct struct_name *ptr;

In C, structure pointers are used in data structures such as linked lists, trees, etc.

4. Function Pointers
Function pointers point to the functions. They are different from the rest of the
pointers in the sense that instead of pointing to the data, they point to the code.
Let’s consider a function prototype – int func (int, char), the function pointer for
this function will be
Syntax
int (*ptr)(int, char);

Note: The syntax of the function pointers changes according to the function
prototype.

5. Double Pointers
In C language, we can define a pointer that stores the memory address of another
pointer. Such pointers are called double-pointers or pointers-to-pointer. Instead of
pointing to a data value, they point to another pointer.
Syntax
datatype ** pointer_name;

Dereferencing Double Pointer


*pointer_name; // get the address stored in the inner level pointer
**pointer_name; // get the value pointed by inner level pointer

Note: In C, we can create multi-level pointers with any number of levels such as –
***ptr3, ****ptr4, ******ptr5 and so on.

6. NULL Pointer
The Null Pointers are those pointers that do not point to any memory location. They
can be created by assigning a NULL value to the pointer. A pointer of any type can
be assigned the NULL value.
Syntax
data_type *pointer_name = NULL;
or
pointer_name = NULL

It is said to be good practice to assign NULL to the pointers currently not in use.

7. Void Pointer
The Void pointers in C are the pointers of type void. It means that they do not have
any associated data type. They are also called generic pointers as they can point
to any type and can be typecasted to any type.
Syntax
void * pointer_name;

One of the main properties of void pointers is that they cannot be dereferenced.
8. Wild Pointers
The Wild Pointers are pointers that have not been initialized with something yet.
These types of C-pointers can cause problems in our programs and can eventually
cause them to crash. If values is updated using wild pointers, they could cause
data abort or data corruption.
Example
int *ptr;
char *str;

9. 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;

10. 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;

Other Types of Pointers in C:


There are also the following types of pointers available to use in C apart from those
specified above:
 Far pointer: A far pointer is typically 32-bit that can access memory outside the
current segment.
 Dangling pointer: A pointer pointing to a memory location that has been
deleted (or freed) is called a dangling pointer.
 Huge pointer: A huge pointer is 32-bit long containing segment address and
offset address.
 Complex pointer: Pointers with multiple levels of indirection.
 Near pointer: Near pointer is used to store 16-bit addresses means within the
current segment on a 16-bit machine.
 Normalized pointer: It is a 32-bit pointer, which has as much of its value in the
segment register as possible.
 File Pointer: The pointer to a FILE data type is called a stream pointer or a file
pointer.

Size of Pointers in C
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.
How to find the size of pointers in C?
We can find the size of pointers using the size of operator as shown in the following
program:

Example: C Program to find the size of different pointer types.


C
// C Program to find the size of different pointers types
#include <stdio.h>

// dummy structure
struct str {
};

// dummy function
void func(int a, int b){};

int main()
{
// dummy variables definitions
int a = 10;
char c = 'G';
struct str x;

// pointer definitions of different types


int* ptr_int = &a;
char* ptr_char = &c;
struct str* ptr_str = &x;
void (*ptr_func)(int, int) = &func;
void* ptr_vn = NULL;

// printing sizes
printf("Size of Integer Pointer \t:\t%d bytes\n",
sizeof(ptr_int));
printf("Size of Character Pointer\t:\t%d bytes\n",
sizeof(ptr_char));
printf("Size of Structure Pointer\t:\t%d bytes\n",
sizeof(ptr_str));
printf("Size of Function Pointer\t:\t%d bytes\n",
sizeof(ptr_func));
printf("Size of NULL Void Pointer\t:\t%d bytes",
sizeof(ptr_vn));

return 0;
}

Output
Size of Integer Pointer : 8 bytes
Size of Character Pointer : 8 bytes
Size of Structure Pointer : 8 bytes
Size of Function Pointer : 8 bytes
Size of NULL Void Pointer : 8 bytes
As we can see, no matter what the type of pointer it is, the size of each and every
pointer is the same.
Now, one may wonder that if the size of all the pointers is the same, then why do
we need to declare the pointer type in the declaration? The type declaration is
needed in the pointer for dereferencing and pointer arithmetic purposes.
C Pointer Arithmetic
The Pointer Arithmetic refers to the legal or valid arithmetic operations that can be
performed on a pointer. It is slightly different from the ones that we generally use
for mathematical calculations as only a limited set of operations can be performed
on pointers. These operations include:
 Increment in a Pointer
 Decrement in a Pointer
 Addition of integer to a pointer
 Subtraction of integer to a pointer
 Subtracting two pointers of the same type
 Comparison of pointers of the same type.
 Assignment of pointers of the same type.
C
// C program to illustrate Pointer Arithmetic

#include <stdio.h>

int main()
{

// Declare an array
int v[3] = { 10, 100, 200 };

// Declare pointer variable


int* ptr;

// Assign the address of v[0] to ptr


ptr = v;

for (int i = 0; i < 3; i++) {

// print value at address which is stored in ptr


printf("Value of *ptr = %d\n", *ptr);

// print value of ptr


printf("Value of ptr = %p\n\n", ptr);

// Increment pointer ptr by 1


ptr++;
}
return 0;
}

Output
Value of *ptr = 10
Value of ptr = 0x7ffcfe7a77a0

Value of *ptr = 100


Value of ptr = 0x7ffcfe7a77a4

Value of *ptr = 200


Value of ptr = 0x7ffcfe7a77a8

C Pointers and Arrays


In C programming language, pointers and arrays are closely related. An array
name acts like a pointer constant. The value of this pointer constant is the address
of the first element. For example, if we have an array named val
then val and &val[0] can be used interchangeably.
If we assign this value to a non-constant pointer of the same type, then we can
access the elements of the array using this pointer.
Example 1: Accessing Array Elements using Pointer with Array Subscript

C
// C Program to access array elements using pointer
#include <stdio.h>

void geeks()
{
// Declare an array
int val[3] = { 5, 10, 15 };

// Declare pointer variable


int* ptr;

// Assign address of val[0] to ptr.


// We can use ptr=&val[0];(both are same)
ptr = val;

printf("Elements of the array are: ");

printf("%d, %d, %d", ptr[0], ptr[1], ptr[2]);

return;
}

// Driver program
int main()
{
geeks();
return 0;
}

Output
Elements of the array are: 5, 10, 15
Not only that, as the array elements are stored continuously, we can pointer
arithmetic operations such as increment, decrement, addition, and subtraction of
integers on pointer to move between array elements.
Example 2: Accessing Array Elements using Pointer Arithmetic

C
// C Program to access array elements using pointers
#include <stdio.h>

int main()
{

// defining array
int arr[5] = { 1, 2, 3, 4, 5 };

// defining the pointer to array


int* ptr_arr = arr;
// traversing array using pointer arithmetic
for (int i = 0; i < 5; i++) {
printf("%d ", *ptr_arr++);
}
return 0;
}

Output
12345
This concept is not limited to the one-dimensional array, we can refer to a
multidimensional array element as well using pointers.
To know more about pointers to an array, refer to this article – Pointer to an Array
Uses of Pointers in C
The C pointer is a very powerful tool that is widely used in C programming to
perform various useful operations. It is used to achieve the following functionalities
in C:
1. Pass Arguments by Reference
2. Accessing Array Elements
3. Return Multiple Values from Function
4. Dynamic Memory Allocation
5. Implementing Data Structures
6. In System-Level Programming where memory addresses are useful.
7. In locating the exact value at some memory location.
8. To avoid compiler confusion for the same variable name.
9. To use in Control Tables.

Advantages of Pointers
Following are the major advantages of pointers in C:
 Pointers are used for dynamic memory allocation and deallocation.
 An Array or a structure can be accessed efficiently with pointers
 Pointers are useful for accessing memory locations.
 Pointers are used to form complex data structures such as linked lists, graphs,
trees, etc.
 Pointers reduce the length of the program and its execution time as well.

Disadvantages of Pointers
Pointers are vulnerable to errors and have following disadvantages:
 Memory corruption can occur if an incorrect value is provided to pointers.
 Pointers are a little bit complex to understand.
 Pointers are majorly responsible for memory leaks in C.
 Pointers are comparatively slower than variables in C.
 Uninitialized pointers might cause a segmentation fault.

Conclusion
In conclusion, pointers in C are very capable tools and provide C language with its
distinguishing features, such as low-level memory access, referencing, etc. But as
powerful as they are, they should be used with responsibility as they are one of the
most vulnerable parts of the language.
Dynamic Memory Allocation in C

Dynamic Memory Allocation refers to the process of allocating or deallocating


memory blocks during a program’s runtime. This is achieved through the use of four
functions:
Method Description
malloc() Allocates a single block of requested memory.
calloc() Allocates multiple blocks of requested memory.
realloc() Reallocates the memory occupied by malloc() or calloc() functions.
free() Frees the dynamically allocated memory.

These functions are located in the <stdlib.h> header file. Dynamic memory
allocation can also be considered a method of utilizing heap memory, where the size
of variables or data structures (such as arrays) can be changed during a program’s
execution with the help of these library functions.
Now, Let us look at the definition, syntax and C example of each of the above-
mentioned functions:
Malloc() Method

malloc() is a function in C that dynamically allocates a block of memory of a


specified size (in bytes) in the heap section of memory during the runtime of a C
program. It can be found in the <stdlib.h> header file.

Syntax
The syntax of malloc() function in C is shown below:
void *malloc(size_t size);
 size is the size in bytes of the memory block to be allocated.
 malloc() returns a pointer to the first byte of the allocated memory block. If the
memory allocation fails, it returns a NULL pointer.
Here’s an example of how to use malloc() to dynamically allocate memory for an
array of integers in C:

#include

#include

int main() {
int n, i;
int *ptr;

printf("Enter the number of elements: ");


scanf("%d", &n);

// Allocate memory for 'n' elements using malloc()


ptr = (int *)malloc(n * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed.\n");
exit(1);
}

printf("Enter the elements: ");


for (i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}

printf("The elements you entered are: ");


for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}

// Deallocate the memory using free()


free(ptr);

return 0;
}

Output:
Enter the number of elements: 5
Enter the elements: 3 5 7 9 11
The elements you entered are: 3 5 7 9 11

In this example, the user is prompted to enter the number of elements they want in
the array. Then, the malloc() function is used to dynamically allocate memory for n
elements of type int. If the memory allocation is successful, the user is prompted to
enter the elements, which are then stored in the dynamically allocated memory.
Finally, the free() function is used to deallocate the memory.

Calloc() Method

The calloc() function in C is used to dynamically allocate a contiguous block of


memory in the heap section. Unlike malloc(), calloc() is used to allocate multiple
blocks of memory, typically to store an array of elements.
Syntax
The syntax of calloc() function in C is shown below:
(cast-type*) calloc(n, size);
 cast-type is the type you want to cast the allocated memory to (for example, int*
or float*)
 n is the number of blocks to be allocated
 size is the size of each block in bytes
Here’s an example of how to use calloc() to dynamically allocate memory for an
array of 5 integers in C:
#include < stdio.h >
#include < stdlib.h >

int main() {
int n, i;
int *ptr;

printf("Enter the number of elements: ");


scanf("%d", &n);

// Allocate memory for 'n' elements using malloc()


ptr = (int *)malloc(n * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed.\n");
exit(1);
}

printf("Enter the elements: ");


for (i = 0; i < n; i++) {
scanf("%d", &ptr[i]);
}

printf("The elements you entered are: ");


for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}

// Deallocate the memory using free()


free(ptr);

return 0;
}

Output:
Memory successfully allocated using malloc.
The elements of the array are: 1, 2, 3, 4, 5,
As we can see in the above example. the program dynamically allocates memory for
an array of 5 integers using the malloc () function and initializes each element with a
value.

Realloc() Method
realloc() is a function in C that allows you to change the size of a previously
allocated memory block. This function can be used to increase or decrease the size
of a block of memory that was previously allocated using either malloc() or calloc().
It can also be used to allocate or de-allocate a memory block on its own completely.
Syntax
The syntax of realloc() function in C is shown below:
void *realloc(void *ptr, size_t size);
 ptr is a pointer to the memory block previously allocated using malloc() or calloc().
 size is the new size for the memory block, in bytes. The function returns a pointer
to the newly allocated memory block. If the reallocation fails, the function
returns NULL.

Here’s an example of how to use realloc() to dynamically allocate memory in C:


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

int main()
{
int *ptr;

// Allocating memory for 5 integers


ptr = (int*)calloc(5, sizeof(int));
if (ptr == NULL) {
printf("Memory not allocated.\n");
exit(0);
}
else {

// Memory has been successfully allocated


printf("Memory successfully allocated using calloc.\n");

// Initializing the allocated memory


for (int i = 0; i < 5; i++)
*(ptr + i) = i + 1;

// Printing the elements of the array


printf("The elements of the array are: ");
for (int i = 0; i < 5; i++)
printf("%d, ", *(ptr + i));
}

return 0;
}
Output:
Elements of the array: 1, 2, 3, 4, 5,
Elements of the array after reallocation: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
This example demonstrates how to use realloc() to increase the size of an array
previously allocated using malloc(). The program first allocates 5 integers
using malloc(), and then reallocates the array to 10 integers using realloc(). The
reallocated array is displayed on the screen.
Free() Method
The free() method in C is used to deallocate a memory block previously allocated
by malloc() or calloc() functions during the execution of the program. It frees up the
occupied memory so that it can be reused again.
Syntax
The syntax of free() function in C is shown below:
void free(void *ptr);
 Here, ptr is a pointer to the memory block that needs to be deallocated.
Here’s an example of how to use free() to dynamically deallocate memory in C:
In the above example, malloc() is used to allocate memory for an integer array of
size 5 and free() is used to de-allocate that memory block.
Malloc() Vs Calloc() Methods
Here is a comparison table between malloc() and calloc() in C:
Feature malloc() calloc()
Allocates single block of requested Allocates multiple blocks of requested
Functionality
memory memory
void *calloc(size_t nmemb, size_t
Syntax void *malloc(size_t size);
size);
Initializes all the allocated memory to
Initial Value Uninitialized
0
Return Pointer to the first byte of the Pointer to the first byte of the allocated
Value allocated memory memory
Time
O(1) O(n) where n is the number of blocks
Complexity
Note: “size_t” is an unsigned integer data type in C that is used to represent the size
of objects in bytes.

You might also like