Download as pdf or txt
Download as pdf or txt
You are on page 1of 82

Data Structures

Prerequisite: C programming and Basic of Mathematics


L-T-P: 2-0-2, Credits: 3
Type: Engineering Essential/ Core Essential

Dr Deepak Gupta
Assistant Professor, SMIEEE
CSED, MNNIT Allahabad, Prayagraj
Email: deepakg@mnnit.ac.in
Course Description

This course introduces the student’s fundamentals of data structures and takes
them forward to software design along with the course on Algorithms. It details
how the choice of data structures impacts the performance of programs for given
software application. This is a precursor to DBMS and Operating Systems. A lab
course is associated with it to strengthen the concepts.
Syllabus
UNIT 1: UNIT-1: Introduction: Basic Terminology, Elementary Data Organization,
Algorithm, Efficiency of an Algorithm, Time and Space Complexity, Asymptotic
notations: Theta, Big-O, and Omega, Time-Space trade-off. Abstract Data Types (ADT)
UNIT II: Arrays: Definition, Single and Multidimensional Arrays, Representation of
Arrays: Row Major Order, and Column Major Order, Application of arrays, Sparse
Matrices and their representations.
Linked Lists: Array Implementation and Dynamic Implementation of Singly Linked
Lists, Doubly Linked List, Circularly Linked List, Operations on a Linked List.
Insertion, Deletion, Traversal, Polynomial Representation and Addition, Generalized
Linked List
Stacks: Abstract Data Type, Primitive Stack operations: Push & Pop, Array and Linked
Implementation of Stack in C, Application of stack: Prefix and Postfix Expressions,
Evaluation of postfix expression, Recursion, Tower of Hanoi Problem, Simulating
Recursion, Principles of recursion, Tail recursion, Removal of recursion
Queues: Abstract Data Type, Operations on Queue: Create, Add, Delete, Full and
Empty, Circular queues, Array and linked implementation of queues in C, Deque and
Priority Queue.
Syllabus

UNIT III: Basic terminology, k-ary trees, Binary Trees, Binary Tree Representation: Array
Representation and Dynamic Representation, Complete Binary Tree, Algebraic Expressions,
Extended Binary Trees, Array and Linked Representation of Binary trees, Tree Traversal
algorithms: In order, Preorder and Post order, Binary Search Trees, Threaded Binary trees,
Traversing Threaded Binary trees, Forest, Huffman algorithm, Heap, B/B+ Tree, AVL tree
UNIT IV: Sequential search, Binary Search, Comparison and Analysis Internal Sorting:
Bubble Sort, Selection Sort, Insertion Sort, Two Way Merge Sort, Heap Sort, Quick Sort,
Hashing
Unit V: Terminology, Sequential and linked Representations of Graphs: Adjacency Matrices,
Adjacency List, Adjacency Multi list, Graph Traversal: Depth First Search and Breadth First
Search, Connected Component, Spanning Trees, Minimum Cost Spanning Trees: Prims and
Kruskal algorithm. Shortest Path algorithm: Dijikstra Algorithm
Books

Text Books:

1. Aaron M. Tenenbaum, YedidyahLangsam and Moshe J. Augenstein “Data


Structures Using C and C++”, PHI

Reference Books:
1. Horowitz and Sahani, “Fundamentals of Data Structures”, Galgotia
Publication
2. Donald Knuth, “The Art of Computer Programming”, vol. 1 and vol. 3.
3. Jean Paul Trembley and Paul G. Sorenson, “An Introduction to Data
Structures with applications”, McGraw Hill
4. R. Kruse et al, “Data Structures and Program Design in C”, Pearson
Education
5. Lipschutz, “Data Structures” Schaum’s Outline Series, TM
Time Table
Introduction
Data: Data is the plural of “datum” (which is rarely used) and may be thought
of as representing numbers, words, facts, figures, images, etc.

Raw Data: A collection of such data which needs further processing by


computers or human being is referred to as Raw Data.
For example, a collection of data related to the marks of students. It may
processed to get the average marks of the class.

Information: If data is arranged in some systematic way then it gets a


structure and becomes meaningful. This meaningful or processed data is
called information.
Knowledge: Knowledge is useful information that supports decision-making.
Data
Abstraction
Information
Abstraction
Knowledge
Fig: Process of Abstraction
Abstraction: The process of providing only the essentials and hiding the
details is known as Abstraction.

Data Type: A data type is a collection of objects and a set of operations that
act on those objects.
For example in C, int data type can take values in a range and operations that
can be performed are addition, subtraction, multiplication, division, bitwise
operations etc.
Data type or Primitive Data types are the predefined data types that are
supported in the programming language. The size depends upon the type of
data type. Primitive Data types can hold only a single value in one specific
location.

Examples of data types are integer, character, float, Boolean, etc.


Data Structures: Data Structure is a particular way of storing and organizing
data in the memory of the computer so that these data can easily be retrieved
and efficiently utilized in the future when required. It is the physical
implementation of Abstract data types (ADTs).

Figure 2: Classifications of Data Structures


Primitive Data Structure/ Types
Primitive data structures are built into a programming language and are
generally considered the most basic data types. They are called primitive
because they are not composed of any other data types.
Examples of primitive data structures include integers, float, characters,
and boolean values.
Non-Primitive Data Structures
These are more complex data types that are composed of primitive data
types or other non-primitive data types. They are also referred to as
composite data types or reference data types.
Examples of non-primitive data structures include arrays, stacks,
queues, and trees.

Based on the structure and arrangement of data, we can divide non-


primitive data structures into two sub-categories:
1. Linear Data Structures

2. Non-Linear Data Structures


Linear Data Structures
A data structure that preserves a linear connection among its data elements is
known as a Linear Data Structure. The arrangement of the data is done
linearly, where each element consists of the successors and predecessors
except the first and the last data elements.
However, it is not necessarily true in the case of memory, as the
arrangement may not be sequential.

Based on memory allocation, the Linear Data Structures are further


classified into two types:

Linear Data Structures

Static Data Structures Dynamic Data Structures


Static Data Structures:
The data structures having a fixed size are known as Static Data Structures.
The memory for these data structures is allocated at the compiler time, and
their size cannot be changed by the user after being compiled; however, the
data stored in them can be altered.
Example: The Array has a fixed size, and its data can be modified later.

An Array is a data structure used to collect multiple data elements of the


same data type into one variable.
• Arrays are declared using the following syntax:
type name[size];

int marks[10];

marks[0] marks[1] marks[2] marks[3] marks[4] marks[5] marks[6] marks[7] marks[8] marks[9]
Dynamic Data Structures:
The data structures having a dynamic size are known as Dynamic Data
Structures. The memory of these data structures is allocated at the run time,
and their size varies during the run time of the code. Moreover, the user
can change the size as well as the data elements stored in these data
structures at the run time of the code.

Example: Linked Lists, Stacks, and Queues are common examples of


dynamic data structures.

Linked Lists
A Linked List is another example of a linear data structure used to store a
collection of data elements dynamically. Data elements in a linked list are
represented by the nodes, connected using links or pointers.
Stack
A Stack is a Linear Data Structure that follows the LIFO (Last In, First Out)
principle that allows operations like insertion and deletion from one end of
the Stack, i.e., Top.
Real-life examples of Stacks are piles of books,
a deck of cards, piles of money, and many
more.

Queue
It follows the First-In-First-Out (FIFO) principle. Elements are inserted at the
back of the queue and removed from the front.
Non-Linear Data Structures
Non-Linear Data Structures are data structures where the data elements are
not arranged in sequential order. Here, the insertion and removal of data are
not feasible in a linear manner. There exists a hierarchical relationship
between the individual data items.

Example: Tree and Graph are common examples of Non-linear data


structures.
Tree:
A Tree is a non-linear data structure and a hierarchy containing a collection
of nodes such that each node of the tree stores a value and a list of references
to other nodes (the "children").
Graph:
A Graph is another example of a Non-Linear Data Structure comprising a
finite number of nodes or vertices and the edges connecting them.
The Graph data structure, G is considered a mathematical structure
comprised of a set of vertices, V, and a set of edges, E as shown below:

G = (V, E)
Algorithm: An algorithm is a finite set of instructions that, if followed,
accomplishes a particular task.
In addition, all algorithms must satisfy the following criteria:
1. Input: There are zero or more quantities that are externally supplied.

2. Output: At least one quantity is produced.


3. Definiteness: Each instruction is clear and unambiguous.

4. Finiteness: The algorithm terminates after a finite number of steps.


5. Effectiveness: Every instruction must be basic enough to be carried out.

Program: A program is a language specific implementation of the algorithm.


It does not satisfy the fourth condition.
Analysis of Algorithms: Analysis of algorithms is required to dictate the
correctness and measure the quantitative efficiency of an algorithm. There may
be many algorithms for solving any problem and we would like to use the most
efficient one.
Analysis of algorithms is required to compare these algorithms and recognize
the best one based on the following criteria:
 Time Complexity
 Space Complexity

 Space Complexity: The space complexity of an algorithm is the amount of


computer memory required during the program execution, as a function of
the input size.

 Time Complexity: The time complexity of an algorithm is basically the


running time of the program as a function of the input size.
The running time of the algorithm is the sum of the running times of each
statement.
The space needed by a program depends on:

 The fixed part includes space needed for storing instructions, constants,
variables, and structured variables.
float abc(float a, float b, float c)
{ Sabc(I) = 0.
return a+b+b*c+(a+b-c)/(a+b)+4
}

 Variable part includes space needed for the recursion stack, and for
structured variables that are allocated space dynamically during the run-
time of the program.
Type Name Number of bytes
Ex: Recursive function for summing a list
Parameter: array pointer list[] 4
of numbers
float rsum(float list[], int n) Parameter: integer n 4
{
return address 4
if (n) return rsum(list, n-1)+list(n-1);
return 0; Total per recursive call 12
}
Srsum(Max_size) = 12*Max_size.
Ex: Iterative function for summing a list of numbers
float sum(float list[], int n)
{
float tempsum = 0;
int i; Sabc(I) = 0.
for(i=0; i<n; i++)
tempsum += list[i];
return tempsum
} Time Complexity

Statement Steps/execution (s/e) Frequencies Total Steps


float sum(float list[], int n) 0 0 0
{ 0 0 0
float tempsum = 0; 1 1 1
int i; 0 0 0
for(i=0; i<n; i++) 1 n+1 n+1
tempsum += list[i]; 1 n n
return tempsum 1 1 1
} 0 0 0
Total 2n+3
Ex: Recursive function for summing a list of numbers
float rsum(float list[], int n)
{
if (n)
return rsum(list, n-1)+list(n-1);
return 0;
}
Statement Steps/execution Frequencies Total Steps
(s/e)
float rsum(float list[], int n) 0 0 0
{ 0 0 0
if (n) 1 n+1 n+1
return rsum(list, n-1) + list(n-1); 1 n n
return list[0]; 1 1 1
} 0 0 0
Total 2n+2
Time Complexity
Ex: Matrix Addition
void add (int a[ ][MAX_SIZE], int b[ ][MAX_SIZE], int c[ ][MAX_SIZE], int rows, int cols)
{
int i, j;
for (i=0; i<rows; i++)
for (j=0; j<cols; j++)
c[i][j] = a[i][j] + b[i][j];
}
Statement Steps/execution Frequencies Total Steps
(s/e)
void add (int a[ ][MAX_SIZE]… 0 0 0
{ 0 0 0
int i, j; 0 0 0
for (i=0; i<rows; i++) 1 rows+1 rows+1
for (j=0; j<cols; j++) 1 rows . (cols+1) rows . cols
+ rows
c[i][j] = a[i][j] + b[i][j]; 1 rows . cols rows . cols
} 0 0 0
Total 2rows . Cols + 2rows +1
Categories of Algorithms

• Constant time algorithms have running time complexity given as O(1)


• Linear time algorithms have running time complexity given as O(n)
• Logarithmic time algorithms have running time complexity given as
O(log n)
• Polynomial time algorithms have running time complexity given as
O(nk) where k>1
• Exponential time algorithms have running time complexity given as
O(2n)

n O(1) O(log n) O(n) O(n log n) O(n2) O(n3) O(2n)

1 1 1 1 1 1 1 2

2 1 1 2 2 4 8 4

4 1 2 4 8 16 64 8

8 1 3 8 24 64 512 256

16 1 4 16 64 256 4,096 65536


Array
Array: An array is a collection of similar data elements of the same type.

Elements of arrays are stored in consecutive memory locations and are


referenced by an index (also known as the subscript).

1-D Arrays are declared using the following syntax:

data_type name[size];
data_type - what kind of values it can store. For example, int, char, float
name - to identify the array
size - the maximum number of values that the array can hold

int marks[10];

marks[0] marks[1] marks[2] marks[3] marks[4] marks[5] marks[6] marks[7] marks[8] marks[9]
The symbolic constants can be used to specify the size of an array. However,
we can’t use the variables for specifying the size of array in the declaration.

#define SIZE 10
main()
{
int size = 15;
int marks[SIZE]; /*valid*/
Int marks[size]={2,3,5}; /*not valid*/

}
Accessing Elements of 1-D Arrays

To access all the elements of an array, we must use a loop.


That is, we can access all the elements of an array by varying the value of the
subscript in the array.

int i, marks[10];
for(i=0;i<10;i++)
printf(“%d ”,marks[i]);

Processing 1-D Arrays


Reading values in marks: for(i=0;i<10;i++)
scanf(“%d ”,&marks[i]);
Display values of marks: for(i=0;i<10;i++)
printf(“%d ”,marks[i]);

Adding all the elements of marks: sum=0;


for(i=0;i<10;i++)
sum+=marks[i];
Calculating the Address of Array Elements:

Address of data element, marks[k] = BA(marks) + w( k – lower_bound)

where, marks is the array


k is the index of the element whose address we have to calculate
BA is the base address of the array marks
w is the word size of one element in memory.
lower_bound = 0

marks[0] marks[1] marks[2] marks[3] marks[4] marks[5] marks[6] marks[7]


1000 1002 1004 1006 1008 1010 1012 1014

marks[4] = 1000 + 2(4 – 0)


= 1000 + 2(4) = 1008
Initialization of 1-D Array

The syntax initialization of an array is:


data_type array_name[size] = {value1, value2, …., valueN};

For example:
int marks[5]={99,78,55,23,91};
int marks[ ]={99,78,55,23,91};

int marks[5]={99,78};
Here, the size of the array is 5 while there are only 2 initializers. The value of
the elements are:
marks[0]=99 marks[1]=78 marks[2]=0 marks[3]=0 marks[4]=0

int marks[5]={99,78,55,23,91,76}; /*error*/


We can’t copy all the elements of an array to another array by assigning it to
the other array. For example
int marks[5]={99,78,55,23,91};
int b[5];
b=marks; /*error*/

We will have to copy all elements of the array one by one by using a for
loop:
for(i=0;i<5;i++)
b[i]=marks[i];
WAP to Read and Display N Numbers using an Array
#include<stdio.h>
#include<conio.h>
int main()
{
int i=0, n, arr[20];
clrscr();
printf(“\n Enter the number of elements : ”);
scanf(“%d”, &n);
printf(“\n Enter the elements : ”);
for(i=0;i<n;i++)
{
printf(“\n arr[%d] = ”, i);
scanf(“%d”, &num[i]);
}
printf(“\n The array elements are ”);
for(i=0; i<n; i++)
printf(“arr[%d] = %d\t”, i, arr[i]);
return 0;
}
Inserting an Element in an Array

Algorithm to insert a new element to the end of an array


Step 1: Set upper_bound = upper_bound + 1
Step 2: Set A[upper_bound] = VAL
Step 3: EXIT

Algorithm INSERT( A, N, POS, VAL) to insert an element VAL at


position POS
Step 1: [INITIALIZATION] SET I = N
Step 2: Repeat Steps 3 and 4 while I >= POS
Step 3: SET A[I + 1] = A[I]
Step 4: SET I = I – 1
[End of Loop]
Step 5: SET N = N + 1
Step 6: SET A[POS] = VAL
Step 7: EXIT
Deleting an Element in an Array

Algorithm to delete a new element to the end of an array


Step 1: Set upper_bound = upper_bound - 1
Step 2: EXIT

Algorithm DELETE( A, N, POS) to delete an element at POS

Step 1: [INITIALIZATION] SET I = POS


Step 2: Repeat Steps 3 and 4 while I <= N-1
Step 3: SET A[I] = A[I + 1]
Step 4: SET I = I + 1
[End of Loop]
Step 5: SET N = N - 1
Step 6: EXIT
Passing Arrays to Functions

1D Arrays For Inter Function


Communication

Passing individual elements Passing entire array

Passing data values Passing addresses

Passing data values


main() void func(int num)
{ {
int arr[5] ={1, 2, 3, 4, 5}; printf("%d", num);
func(arr[3]); }
}
Passing addresses

main() void func(int *num)


{ {
int arr[5] ={1, 2, 3, 4, 5}; printf("%d", *num);
func(&arr[3]); }
}

Passing the entire array


main() void func(int arr[5])
{ {
int arr[5] ={1, 2, 3, 4, 5}; int i;
func(arr); for(i=0;i<5;i++)
} printf("%d", arr[i]);
}
void func(int []);
main()
{
int i, arr[6]={1,2,3,4,5,6};
func(arr);
printf(“Contents of array are now: “);
for(i=0; i<6; i++)
printf(“%d”,arr[i]);
printf(“/n”);
}

void func(int val[])


{
int sum=0,i;
for(i=0;i<6;i++)
{
val[i]=val[i]*val[i];
sum+=val[i];
}
printf(“The sum of squares = %d\n”, sum);
}
Output:

The sum of squares = 91


Contents of array are now: 1 4 9 16 25 36
Two-dimensional Arrays
A two-dimensional array is specified using two subscripts where one
subscript denotes row and the other denotes column.
A two-dimensional array is an array of one-dimensional arrays in C which is
declared as:
data_type array_name[row_size][column_size];

Therefore, a two-dimensional m×n array is an array that contains m×n data


elements and each element is accessed using two subscripts, i and j, where
i<=m and j<=n:
int marks[3][5];
Col 0 Col 1 Col2 Col 3 Col 4
Rows/Cols
Row 0 Marks[0][0] Marks[0][1] Marks[0][2] Marks[0][3] Marks[0][4]
Row 1 Marks[1][0] Marks[1][1] Marks[1][2] Marks[1][3] Marks[1][4]
Row 2 Marks[2][0] Marks[2][1] Marks[2][2] Marks[2][3] Marks[2][4]
Memory Representation of a 2D Array
There are two ways of storing a 2-D array in memory. The first way is row-
major order and the second is column-major order.
In the row-major order, the elements of the first row are stored before the
elements of the second and third rows. That is, the elements of the array
are stored row by row where n elements of the first row will occupy the
first nth locations.

(0,0) (0, 1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3)

In the column-major order, the elements of the first column are stored
before the elements of the second and third columns. That is, the elements
of the array are stored column by column where n elements of the first
column will occupy the first nth locations.

(0,0) (1,0) (2,0) (3,0) (0,1) (1,1) (2,1) (3,1) (0,2) (1,2) (2,2) (3,2)
Initializing Two-dimensional Arrays
A two-dimensional array is initialized in the same way as a 1-D array is

initialized.

For example,

int marks[2][3]={90, 87, 78, 68, 62, 71};

int marks[2][3]={{90,87,78},{68, 62, 71}};


Passing 2D Arrays to Functions
There are three ways of passing two-dimensional arrays to a function.

2D Array for Inter Function Communication

Passing individual elements Passing a row Passing the entire 2D array

Passing individual elements


main() void func(int num)
{ {
int arr[2][3] ={{1, 2, 3}, {4, 5, 6}}; printf("%d", num);
func(arr[1][3]); }
}
Passing a row
main() void func(int arr[])
{ {
int arr[2][3]= ( {1, 2, 3}, {4, 5, 6} }; int i;
func(arr[1]); for(i=0;i<3;i++)
} printf("%d", arr[i] * 10);
}

Passing the entire array


main() void func(int arr[][])
{ {
int arr[2][3]= ( {1, 2, 3}, {4, 5, 6} }; int i;
func(arr); for(i=0;i<2;i++)
} for(j=0;j<3;j++)
printf("%d", arr[i][j]);
}
Pointers and Arrays
The name of an array is a pointer that points to the first element of the array.

int *ptr;
ptr = &arr[0];

If pointer variable ptr holds the address of the first element in the array, then
the address of the successive elements can be calculated by writing ptr++.

int *ptr = &arr[0];


ptr++;
printf (“The value of the second element in the array is %d”, *ptr);
Arrays of Pointers
An array of pointers can be declared as:
int *ptr[10];
The above statement declares an array of 10 pointers where each of the
pointer points to an integer variable. For example, look at the code given
below. int *ptr[10];
int p=1, q=2, r=3, s=4, t=5;
Can you tell what will be the output
ptr[0]=&p;
of the following statement?
ptr[1]=&q;
printf(“\n %d”, *ptr[3]);
ptr[2]=&r;
ptr[3]=&s;
ptr[4]=&t
The output will be 4 because ptr[3] stores the address of integer
variables and *ptr[3] will therefore print the value of s that is 4.
Pointers and 2D Arrays
Individual elements of the array mat can be accessed using either:
mat[i][j] or *(*(mat + i) + j) or*(mat[i]+j);

Pointer to a one-dimensional array can be declared as:


int arr[ ]={1,2,3,4,5};
int *parr;
parr=arr;

Similarly, pointer to a two-dimensional array can be declared as:


int arr[2][2]={{1,2},{3,4}};
int (*parr)[2];
parr=arr;
Multi-dimensional Arrays
A multi-dimensional array is an array of arrays.
We have one index in a single-dimensional array, and two indices in a
two-dimensional array, in the same way we have n indices in a n-
dimensional array or multi-dimensional array.

In a multi-dimensional array, a particular element is specified by using n


subscripts as A[I1][I2][I3]…[In], where
I1<=M1 I2<=M2 I3 <= M3 ……… In <= Mn
Initializing Multi-dimensional Arrays

A multi-dimensional array is declared and initialized the same way as we


declare and initialize one- and two-dimensional arrays.

A pointer to a three-dimensional array can be declared as:

int arr[2][2][2]={1,2,3,4,5,6,7,8};
int (*parr)[2][2];
parr=arr;

We can access an element of a three-dimensional array by writing:

arr[i][j][k]= *(*(*(arr+i)+j)+k)
Assigning address to a pointer variable
When we declare a pointer variable it contain garbage value i.e. it may be
pointing anywhere in the memory.
int *iptr, age=30;
float *fptr, sal = 1500.5;
iptr = &age;
fptr = &sal;

One can initialize the pointer at the time of declaration, but the variable
should be declared before the pointer. For example:
int age=30;
int *iptr = &age;
Dereferencing Pointer Variables
One can access a variable indirectly using pointers. For this, we will use the
indirection operator (*). The indirection operator can be read as ‘value at
the address’.
int a = 87;
float b = 4.5;
int *p1 = &a;
float *p2 = &b;
Here,
*p1 = 9; is equivalent to a=9;
(*p1)++; is equivalent to a++;
x=*p2 + 10; is equivalent to x=b + 10;
printf(“%d %f”, *p1, *p2); is equivalent to printf(“%d %f”, a, b);
scanf(“%d %f”, p1, p2); is equivalent to scanf(“%d %f”, &a, &b);
Pointer to Pointer
The syntax of declaring a pointer to pointer is as:
data_type **ptr;
Example:
int a = 5; ppa pa a
int *pa = &a; 3000 2000 5
int **ppa = &pa; 4000 3000 2000
Program to print the value and address of elements of an array using
pointer notation:

# include <stdio.h>
main()
{
int i, arr[5] = {5, 10, 15, 20, 25};
for(i=0; i<5; i++)
printf(“value of arr[%d] = %d\t”, i, *(arr + i) );
printf(“Address of arr[%d] = %p\n”, i, (arr + i) );

printf(“/n”);
}
Pointers and Functions
Call by value
# include <stdio.h>
void value(int x, int y);
main( )
{
int a=5; b=8;
printf(“Before calling the function, a = %d and b=%d\n”, a, b);
value(a, b);
printf(“After calling the function, a = %d and b=%d\n”, a, b);
}
void value(int x, int y)
{
x++;
y++;
printf(“Inside function x = %d, y = %d\n”, x, y);

}
Pointers and Functions
Call by reference
# include <stdio.h>
void value(int *p, int *q);
main( )
{
int a=5; b=8;
printf(“Before calling the function, a = %d and b=%d\n”, a, b);
value(&a, &b);
printf(“After calling the function, a = %d and b=%d\n”, a, b);
}
void value(int *p, int *q)
{
(*p)++;
(*q)++;
printf(“Inside function *p = %d, *q = %d\n”, *p, *q);

}
Returning more than one value from a function:
Program to show how to return more than one value from a function
using call by reference:
# include <stdio.h>
func (int x, int y, int *ps, int *pd, int *pp)
main()
{
int a, b, sum, diff, prod;
a = 6;
b = 4;
func(a, b, &sum, &diff, &prod);
printf(“Sum = %d, Difference = %d, Product = %d\n”, sum, diff, prod);
}

func(int x, int y, int *ps, int *pd, int *pp)


{
*ps = x+y;
*pd = x-y;
*pp = x*y;
}
Function Returning Pointer
The syntax of declaration of such type of function is:
data_type *func (type, type 2, …);
For example: int *func (int, char);

/*Program to show a function that returns a pointer*/

# include <stdio.h>
int *func (int *p, int n);
main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, n, *ptr;
n = 5;
ptr = func(arr, n);
printf(“arr = %p, ptr = %p, *ptr = %d\n”, arr, ptr, *ptr);
}
int *func(int *p, int n)
{
p = p+n;
return p;
}
Applications of Arrays

•Arrays are widely used to implement mathematical vectors, matrices and


other kinds of rectangular tables.
•Many databases include one-dimensional arrays whose elements are
records.
•Arrays are also used to implement other data structures like heaps, hash
tables, deques, queues, stacks and string. We will read about these data
structures in the subsequent chapters.
•Arrays can be used for dynamic memory allocation.
Dynamic Memory Allocation

Two types of problems may occur during static memory allocation:

The number of values to be stored is less than the size of the array and
hence there is a wastage of memory.

Our program fails if we want to store more values than the size of an array.

To overcome these problems we should be able to allocate memory at run


time. The process of allocating memory at the time of execution is called
dynamic memory allocation.
malloc( )

Declaration: void * malloc (size_t size);

malloc( ) returns a pointer to the first byte of allocated memory. The return
pointer is of type void, which can be type cast to the appropriate type of
pointer.

It is generally used as:

ptr = (datatype *) malloc (specified size);


Here ptr is a pointer to type datatype and the specified size is the size in bytes
required to be reserved in memory. For example:
int *ptr;
ptr = (int *) malloc (12); This allocates 12 contiguous byte of memory
space and memory contains garbage value.
One can use sizeof operator to make the program portable and readable.

ptr = (int *) malloc (5 * sizeof(int));

If there is not sufficient memory available in the heap then malloc returns
NULL.

ptr = (int *) malloc (5 * sizeof(int));


if(ptr==NULL)
printf(“Sufficient memory is not available”);
/*Program to understand dynamic memory allocation*/
# include <stdio.h>
# include <stdlib.h>
main()
{
int *p, n, i;
printf(“enter the number of element to be entered”);
scanf(“%d”, &n);
p = (int*)malloc(n*sizeof(int));
if(ptr==NULL)
{
printf(“Sufficient memory is not available\n”);
exit(1);
}
for(i=0; i<n; i++)
{
printf(“Enter an integer: “);
scanf(“%d”, p+i);
}
for(i=0; i<n; i++)
printf(“%d\t”, *(p+i));
}
calloc( )

Declaration: void * calloc(size_t n, size_t size);

calloc( ) returns a pointer to the first byte of allocated memory. It is similar


to malloc function except for two differences.
• The first one is that it takes two arguments.
ptr = (int *) calloc(5, sizeof(int));
• The second difference between calloc( ) and malloc( ) is that the
memory allocated by malloc( ) contains garbage value while the
memory allocated by calloc( ) is initialized to zero.
realloc( )

Declaration: void * realloc(void *ptr, size_t newsize);

The function realloc( ) is used to change the size of memory block. This
function takes two arguments:
First is a pointer to the block of memory that was previously allocated by
malloc( ) or calloc( ).
Second one is the new size for that block.
For example:
ptr = (int *)malloc(size);
ptr = (int*)realloc(ptr, newsize);
/*Program to understand the use of realloc()*/
# include <stdio.h>
# include <stdlib.h>
main()
{
int *ptr, i;
ptr = (int*)malloc(5*sizeof(int));
if(ptr = = NULL)
{
printf(“Sufficient memory is not available\n”);
exit(1);
}
printf(“Enter 5 integers : ”);
for(i=0; i<5; i++)
scanf(“%d”, ptr+i);
/* Allocate memory for 4 more integers*/
ptr = (int*)realloc(ptr, 9*sizeof(int));
if(ptr = = NULL)
{
printf(“Sufficient memory is not available\n”);
exit(1);
}
printf(“Enter 4 more integers : ”);
for(i=5; i<9; i++)
scanf(“%d”, ptr+i);
for(i=0; i<9; i++)
printf(“%d\t”, *(ptr+i));
}
free( )

Declaration: void free(void *p);

For example:
free(ptr);
Structure
Structure is a user-defined data type that can store related information
(even of different data types) together.

A structure is declared using the keyword struct followed by a structure


name.

All the variables of a structure are declared within the structure.

A structure type is defined by using the following syntax:


struct struct-name
{ data_type var-name;
data_type var-name;
...
};
Let us take an example of defining a structure template:
struct student {
int rollno;
char name[20];
float marks;
};
The structure definition does not allocate any memory. It just gives a
template that conveys to the compiler how the structure is laid out in
memory and gives details of the members.

Memory is allocated for the structure when we declare a variable of the


structure. For example, we can define a variable of student by writing
struct student stud1;
Declaring Structure Variables
1. With structure definition
2. Using the structure tag
1. With structure definition
struct student {
int rollno;
char name[20];
float marks;
}stu1,stu2,stu3;
2. Using the structure tag struct student {
int rollno;
char name[20];
float marks;
};
struct student stu1,stu2;
struct student stu3;
Initialization of Structure Variables
Initializing a structure means assigning some constants to the members of
the structure.
When the user does not explicitly initializes the structure then C
automatically does that.
For int and float members, the values are initialized to zero and char and
string members are initialized to ‘\0’ by default.

The initializers are enclosed in braces and are separated by commas.


The general syntax to initialize a structure variable is given as follows:
struct struct_name
{
data_type member_name 1;
data_type member_name 2;
data_type member_name 3;
…………………………………………..
}struct_var= {constant1, constant2, constant3, ….};
Accessing the Members of a Structure
Each member of a structure can be used just like a normal variable, but its
name will be a bit longer.

A structure member variable is generally accessed using a ‘.’ (dot


operator).
The syntax of accessing a member of a structure is:
struct_var.member_name
For example, to assign value to the individual data members of the
structure variable stud1, we may write:
stud1.rollno = 01;
Accessing the Members of a Structure
We can assign a structure to another structure of the same type.

For example, if we have two structure variables stud1 and stud2 of type
struct student
struct student stud1 = {01, "Rahul", "BCA", 45000};
struct student stud2;
Then to assign one structure variable to another, we will write:
stud2 = stud1;
Array of Structures
The array of structures can be defined as:
struct student stud[10];
For example:
struct student stuarr[3] = {
{“Mary”, 12, 98.5},
{“John”, 11, 97.5},
};
Nested Structures
A structure can be placed within another structure.
Such a structure that contains another structure as its member is called a
nested structure.

typedef struct typedef struct typedef struct


{ char first_name[20]; { int dd; { int rollno;
char mid_name[20]; int mm; NAME name;
char last_name[20]; int yy; DATE DOB;
} NAME; }DATE; }student;

To assign values to the structure fields, we will write:


struct student stud1;
stud1.name.first_name = “Janak”;
stud1.DOB.dd = 15;
stud1.DOB.mm = 03;
stud1.DOB.yy= 1990;
Passing Individual Structure Members to a Function
To pass any individual member of the structure to a function we must use
the direct selection operator to refer to the individual members for the
actual parameters.
The called program does not know if the two variables are ordinary
variables or structure members.
typedef struct
{ int x;
int y;
}POINT;
POINT p1={2,3};
display(p1.x, p1.y); //passing members of p1 to function display()
Passing a Structure to a Function
When a structure is passed as an argument, it is passed using call by value
method. That is a copy of each member of the structure is made.
The general syntax for passing a structure to a function and returning a
structure can be given as:
struct struct_name func_name(struct struct_name struct_var);

typedef struct
{ int x;
int y;
}POINT;
POINT p1={2,3};
display(p1); //passing entire structure p1 to function display()
Passing Structures through Pointers
C allows to create a pointer to a structure.
Like in other cases, a pointer to a structure is never itself a structure, but
merely a variable that holds the address of a structure.

The syntax to declare a pointer to a structure can be given as:


struct struct_name
{
data_type member_name1;
data_type member_name2;
.....................................
}*ptr;

OR

struct struct_name *ptr;


Passing Structures through Pointers
For our student structure we can declare a pointer variable by writing:
struct student *ptr_stud, stud;
The next step is to assign the address of stud to the pointer using the
address operator (&). So to assign the address, we will write:
ptr_stud = &stud;

To access the members of the structure, one way is to write:


(*ptr_stud).roll_no;

An alternative to the above statement can be used by using ‘pointing-to’


operator (->):
ptr_stud->roll_no = 01;
Self-referential Structures
Self-referential structures are those structures that contain a reference to
data of its same type.
That is, a self-referential structure contains a pointer to data that is of the
same type as that of the structure.
struct node
{ int val;
struct node *next;
};
Here the structure node contains two types of data: an integer val and next
that is a pointer to a node.
Union
Similar to structures, a union is a collection of variables of different data
types.

The difference between a structure and a union is that in unions,


information can be stored in one field at any one time.
Unions are used to save memory. They are useful for applications that
involve multiple members, where values need not be assigned to all the
members at any one time.

The syntax for union declaration can be given as :


union union-name
{
data_type var-name;
data_type var-name;
................................
};
Initializing Unions
#include <stdio.h>
typedef union POINT2
{
int x;
int y;
};
int main()
{
POINT2 P2;
P2. x = 4;
printf(“\n The x coordinate of P2 is %d”, P2.x);
P2.y = 5;
printf(“\n The y coordinate of P2 is %d”, P2.y);
return 0;
}
Arrays of Unions
#include <stdio.h>
union POINT
{
int x, y;
};
int main()
{ int i;
union POINT points[3];
points[0].x = 2; points[0].y = 3; points[1].x = 4;
points[1].y = 5; points[2].x = 6; points[2].y = 7;
for(i=0;i<3;i++)
printf(“\n Coordinates of Point[%d] are %d and %d”, i, points[i].x,
points[i].y);
return 0;
}
Unions inside Structures
#include <stdio.h>
struct student
{ union
{ char name[20];
int roll_no;
};
int marks;
};
/* in main() function*/
printf(“\n You can enter the name or roll number of the student”);
printf(“\n Do you want to enter the name? (Y or N): ”);
gets(choice);
if(choice==‘y’ || choice==‘Y’)
{ printf(“\n Enter the name: ”);
gets(stud.name);
}
else
{ printf(“\n Enter the roll number: ”);
scanf(“%d”, &stud.roll_no);
}

You might also like