CS151 Unit3 Notes
CS151 Unit3 Notes
CS151 Unit3 Notes
Multi-Dimensional Array
A multi-dimensional array is an array with more than one level or dimension. It might be 2-
Dimensional and 3-Dimensional and so on.
Consider the example of storing marks of 5 students in some particular subject A. We can define,
Consider the example of storing marks of 5 students in 3 different subjects A, B and C. We can say,
Version-1:
Version-2:
Now, rather than storing marks this way in One - Dimensional Array, we can use Two - Dimensional
array as we have two dimensions involved in this particular example: student and subject marks
Let us consider the above example of storing 5 students marks in three different subjects. int
marks[5][3] = {{90, 95, 91}, {95, 90, 92}, {99, 91, 93}, {100, 99, 95}, {89, 94, 100} };
Declaration of a 2D Array
data_type array_name[size_1][size_2];
Arr
X X X X X X X X
2000 2004 2008 2012 2016 2020 2024 2028
Initialization of a 2D Array: Compiler knows the array size based the array elements and
allocates memory
Without knowing the number of columns in each row – it is impossible to locate an element in
2D array.
As two - dimensional array itself is an array, elements are stored in contiguous memory
locations. And since elements are stored in contiguous memory locations, there are two possibilities.
int arr[3][4] = {{11, 22, 33, 44}, {55, 66, 77, 88}, {99, 100, 111, 121}};
Row major Ordering: All elements of one row are stored followed by all elements of the next row
and so on.
Column Major Ordering: All elements of one column are stored followed by all elements of the
next column and so on.
Consider, int arr[3][4] = {{11, 22, 33, 44}, {55, 66, 77, 88}, {99, 100, 111, 121}};
If the size of integer is 4 bytes in the system and the base address is 5000, then address of arr[1][2]
can be found by 5000+((1*4)+2)*4 = 5000+6*4 = 5000+24 = 5024.
Consider, int arr[3][4] = {11, 22, 33, 44, 55, 66, 77, 88, 99, 100, 111, 121};
If the size of integer is 4 bytes in the system and the base address is 5000, then address of arr[1][5]
can be found by 5000+((1*4)+5)*4 = 5000+9*4 = 5000+36 = 5036.
int a[100][100];
int n; int m;
scanf("%d %d",&n,&m);
printf("Enter %d elements",n*m);
for(int j= 0;j<m;j++)
scanf("%d",&a[i][j]);
}
5 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Multi-Dimentional Arrays 2021
for(int i = 0;i<n;i++)
for(int j= 0;j<m;j++)
printf("%d\t",a[i][j]);
printf("\n");
Array name is a pointer to a row. The expression a + 3 is a pointer to the third row. *(a + 3)
becomes a pointer to the zeroth element of that row. Then +1 becomes a pointer to the nextelement
in that row. To get the element in that location, dereference the pointer.
// If above statement is fine, what happens when p1[7] is accessed and p1[2][1] is accessed
Since subscript([ ]) have higher precedence than indirection(*), it is necessary to have parentheses
for indirection operator and pointer name. Here the type of p is ‘pointer to an array of 2 integers’.
Note : The pointer that points to the 0th element of array and the pointer that points to the whole
array are totally different.
int arr[4][3]={33,44,11,55,88,22,33,66,99,11,80,9};
int (*p)[3]=arr;
printf(“%d\n”,p[2][1]);
printf(“%d\n”,*(*(p+2)+1));
printf(“%d %d\n”,sizeof(*p),sizeof(arr)); //size of the array pointing to 0th row and size of the
entire 2D array
Let us try to read and display 2D array using functions. Client code is as below.
int main()
int a[100][100];
int m1,n1;
read(a,m1,n1);
printf("elements of A are\n");
display(a,m1,n1);
Case 2: Declaration and definition of the function with column size not same as what is given
in the declaration of the array in the client code
Case 3: Declaration and definition of the function with column size same as what is given in
the declaration of the array in the client code
Case 4: If the order of parameters is changed as below in declaration and definition of the
function, we need to change the client code. Changing the client code is not a good
programmers habit. Because interface cannot be changed. Implementation can change. So
refer to case 5.
int i,j;
for(i = 0;i<m;i++)
scanf("%d",&a[i][j]);
int i,j;
for(i = 0;i<m;i++)
printf("%d\t",a[i][j]);
printf("\n");
Case 6: Array degenerates to a pointer at run time. So how about using pointer to an array as
a parameter.
int i,j;
for(i = 0;i<m;i++)
scanf("%d",&a[i][j]);
int i,j;
for(i = 0;i<m;i++)
printf("%d\t",a[i][j]);
printf("\n");
Let us write a function to add, subtract and multiply two matrices. Display
appropriate message when these two matrices are not compatible for these
operations.
void multiply(int n1; int n2; int (*a)[n1],int (*b)[n2],int m1,int n1,int n2,int (*c)[n2]);
int main()
int a[100][100];
int b[100][100];
int c[100][100];
int m1,n1;
int m2,n2;
scanf("%d%d",&m1,&n1);
read(a,m1,n1);
scanf("%d%d",&m2,&n2);
read(b,m2,n2);
printf("elements of A are\n");
display(a,m1,n1);
printf("elements of B are\n");
display(b,m2,n2);
add(a,b,m1,n1,c);
display(c,m2,n2);
subtract(a,b,m1,n1,c);
14 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Multi-Dimentional Arrays 2021
display(c,m2,n2);
else
if (n1==m2)
multiply(a,b,m1,n1,n2,c);
display(c,m1,n2);
else
return 0;
for(int i = 0;i<m;i++)
for(int j= 0;j<n;j++)
c[i][j] = a[i][j]+b[i][j];
for(int i = 0;i<m;i++)
for(int j= 0;j<n;j++)
}
16 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Multi-Dimentional Arrays 2021
void multiply(int n1; int n2; int (*a)[n1],int (*b)[n2],int m1,int n1,int n2,int (*c)[n2])
for(int i = 0;i<m1;i++)
for(int j= 0;j<n2;j++)
c[i][j] = 0;
for(int i=0;i<m1;i++)
for(int j=0;j<n2;j++)
int sum=0;
for(int k=0;k<n1;k++)
sum=sum+a[i][k]*b[k][j];
}
17 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Multi-Dimentional Arrays 2021
c[i][j]=sum;
Let us write a program to take n names from the user and print it. Each name
can have maximum of 20 characters.
#include<stdio.h>
int main()
/*
char name1[20]; //Can store one name with maximum of 20 characters including '\0'
// But can we store n names under the same variable name???? --- Yes. Use 2D array
*/
scanf("%d", &n);
int i;
18 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Multi-Dimentional Arrays 2021
scanf("%[^\n]s",names[i]);
printf("%s\n",names[i]);
return 0;
Structures
Structure is a user-defined data type in C language which allows us to combine data of
different types together. It helps to construct a complex data type which is more meaningful. It
is often convenient to have a single name with which to refer to a collection of related items
of different types. Structures provide a way of storing many different values in variables of
potentially different types under the same name. Structures are generally useful whenever a lot
of data needs to be grouped together. For instance, they can be used to hold records from a
database or to store information about contacts in an address book.
Why Structures?
Need of Structure is discussed through below mentioned programs.
Let us try to interactively store the Roll_number, Total_marks and Name of 20 students.
char names[20][15];
int marks[20];
int roll_num[20];
for(int i=0;i<20;i++)
{ printf("Enter Roll_number, marks and name\n");
scanf("%d",&roll_num[i]);
scanf("%d",&marks[i]);
scanf("%[^\n]s",names[i]);
}
Display the entries and check whether this code works.
By using separate arrays for each of the details of students, nowhere we saying all these
three are related. So, we need Structures.
Introduction to Structures
A structure creates a data type that can be used to group items of possibly different
types into a single type. Grouping of attributes into a single entity is known as Structure.
Creating a new type decides the binary layout of the type. Order of fields and the total size of a
variable of that type is decided when the new type is created.
Characteristics/properties of structures:
A user defined data type/non-primary/secondary
Contains one or more components – Generally known as data members. These are
named ones.
Collection of homogeneous or heterogeneous data members
Size of a structure depends on implementation. Memory allocation would be at least
equal to the sum of the sizes of all the data members in a structure. Offset is decided at
compile time.
Compatible structures may be assigned to each other.
Any member of a structure can be accessed using the pointer to a structure as:
pointer_variable->member_name
Suppose, s2 is the pointer to structure variable and we want to access roll_no member of s2.
Then, it can be accessed as:
s2->roll_no
Version 2:
struct student s2 = {44, "abc", 100}; // Initialization
printf("%d %d %s", s2.marks, s2.roll_no, s2.name); // 100 44 abc
Version 3:
struct student s3 = {44, "abc"}; // Partial Initialization
// Explicitly few members are initialized. others are initialized to default value
printf("%d %d %s", s3.marks, s3.roll_no, s3.name); // 0 44 abc
Below programs are run on Windows7 machine and 32 bit GCC compiler
struct test
{ int i;
char j;
};
struct test1
{ char j;
int i;
};
struct test2
{ char k;
char
j; int i;
};
struct test3
{ int i;
char k;
int j;
};
int main()
{
printf("size of the structure is %lu\n",sizeof(struct test)); //8bytes 4+4
Comparison of Structures
struct testing
{ int a; float b; char c;
};
int main()
{ struct testing s1 = {44, 4.4, 'A'};
struct testing s2 = {44, 4.4, ‘A’};
//printf(“%d”, s1== s2); // Compiletime Error.
// == operator cannot be applied on structures for comparing
// Think about s1- s2
// Write a function to check for equality of every member of the structure
printf(“%d”,is_equal(s1,s2)); // 1 // all fields are equal
struct testing s3 = {33, 3.3, ‘A’};
printf(“%d”,is_equal(s1,s3)); // 0 //first condition itself fails
return 0;
}
int is_equal(struct testing s1, struct testing s2)
{ return (s1.a == s2.a && s1.b == s2.b && s1.c == s2.c); }
int main()
{ struct testing s1 = {44,4.4, 'A'};
struct testing s2 = s1;
s1.a = 55;
printf("%d\n",s1.a);
printf("%d\n",s2.a);
return 0;
}
Version 3:
Assume the player structure contains many data members. In this case, can we just use
structure variable to disp function? If we do so, this is considered as a bad practice. As
every member of argument is copied to every member of parameter, it requires more
space and more time to copy.
Also, when we are very sure that we do not want to make any changes to the argument,
parameter can be a pointer to constant structure
int main()
{
struct player p1={20,"sachin"};
printf("before change %s",p1.name);
p1=modify(p1);
printf("after change %s",p1.name);
return 0;
}
When the function modify() returns a changed parameter by value, it is copied to a temporary.
Then in the client code, the temporary is assigned to p1.
struct player modify(struct player p)
{
strcpy(p.name,"Sourav");
return p;
}
Usage of typedef
typedef is a keyword which is used to give a type, a new name. Used to assign alternative
names to existing data types. It is used to provide an interface for the client. Once a new name
is created using typedef, the client may use this without knowing the underlying type.
I want to store the age of a person. Best way to define a variable is int age = 80; Alternatively,
you can also say, typedef int age; //another name for int is age.
Then age a = 80;
Can we say age age = 80? // Think about this.
If we know how to declare a variable, then prefixing the declaration with typedef makes that a
new name.
int b[10]; // b is an array variable to store 10 integers
typedef int c[10]; // c is a name for an array of 10 integers
typedef is mostly used with user defined data types when names of the data types become
slightly longer and complicated to use in programs.
Version 1:
struct player
{ int id;
char name[20];
};
Version 2: You can include typedef when defining a new type itself.
typedef struct player
{ int id;
Nested Structures
Structure written inside another structure is called as nesting of two structures. It can bed
done in two ways.
1. Separate structures: Here, we create two structures, but the dependent structure should be
used inside the main structure as a member. Consider the following example.
struct dob
{
int date;
int month;
int year;
};
struct student // template declaration which describes how student entity looks like
{
int roll_no; // these three are members of the structure
char name[20];
int marks;
struct dob d1; //using structure variable of dob inside student
};
It can be understood from example that dob (date of birth) is the structure with members
as date, month and year. Dob is then used as a member in Student structure.
struct student // template declaration which describes how student entity looks like
{
int roll_no; // these three are members of the structure
char name[20];
int marks;
struct dob //declaring structure dob inside student
{
int date;
int month;
int year;
}d1;
};
Accessing Nested Structure Members: We can access the member of the nested structure by
using Outer_Structure.Nested_Structure.member as given below:
int main()
{
struct student s1 = {24, "John", 78, {20, 03, 2000}}; //Declaration and Initialization
printf("\n Roll no. = %d, Name= %s, Marks = %d, dob = %d.%d.%d ", s1.roll_no,
s1.name,s1.marks, s1.d1.date,s1.d1.month, s1.d1.year);
return 0;
}
Structures
Structure is a user-defined data type in C language which allows us to combine data of
different types together. It helps to construct a complex data type which is more meaningful. It
is often convenient to have a single name with which to refer to a collection of related items
of different types. Structures provide a way of storing many different values in variables of
potentially different types under the same name. Structures are generally useful whenever a lot
of data needs to be grouped together. For instance, they can be used to hold records from a
database or to store information about contacts in an address book.
Why Structures?
Need of Structure is discussed through below mentioned programs.
Let us try to interactively store the Roll_number, Total_marks and Name of 20 students.
char names[20][15];
int marks[20];
int roll_num[20];
for(int i=0;i<20;i++)
{ printf("Enter Roll_number, marks and name\n");
scanf("%d",&roll_num[i]);
scanf("%d",&marks[i]);
scanf("%[^\n]s",names[i]);
}
Display the entries and check whether this code works.
By using separate arrays for each of the details of students, nowhere we saying all these
three are related. So, we need Structures.
Introduction to Structures
A structure creates a data type that can be used to group items of possibly different
types into a single type. Grouping of attributes into a single entity is known as Structure.
Creating a new type decides the binary layout of the type. Order of fields and the total size of a
variable of that type is decided when the new type is created.
Characteristics/properties of structures:
A user defined data type/non-primary/secondary
Contains one or more components – Generally known as data members. These are
named ones.
Collection of homogeneous or heterogeneous data members
Size of a structure depends on implementation. Memory allocation would be at least
equal to the sum of the sizes of all the data members in a structure. Offset is decided at
compile time.
Compatible structures may be assigned to each other.
Any member of a structure can be accessed using the pointer to a structure as:
pointer_variable->member_name
Suppose, s2 is the pointer to structure variable and we want to access roll_no member of s2.
Then, it can be accessed as:
s2->roll_no
Version 2:
struct student s2 = {44, "abc", 100}; // Initialization
printf("%d %d %s", s2.marks, s2.roll_no, s2.name); // 100 44 abc
Version 3:
struct student s3 = {44, "abc"}; // Partial Initialization
// Explicitly few members are initialized. others are initialized to default value
printf("%d %d %s", s3.marks, s3.roll_no, s3.name); // 0 44 abc
Below programs are run on Windows7 machine and 32 bit GCC compiler
struct test
{ int i;
char j;
};
struct test1
{ char j;
int i;
};
struct test2
{ char k;
char
j; int i;
};
struct test3
{ int i;
char k;
int j;
};
int main()
{
printf("size of the structure is %lu\n",sizeof(struct test)); //8bytes 4+4
Comparison of Structures
struct testing
{ int a; float b; char c;
};
int main()
{ struct testing s1 = {44, 4.4, 'A'};
struct testing s2 = {44, 4.4, ‘A’};
//printf(“%d”, s1== s2); // Compiletime Error.
// == operator cannot be applied on structures for comparing
// Think about s1- s2
// Write a function to check for equality of every member of the structure
printf(“%d”,is_equal(s1,s2)); // 1 // all fields are equal
struct testing s3 = {33, 3.3, ‘A’};
printf(“%d”,is_equal(s1,s3)); // 0 //first condition itself fails
return 0;
}
int is_equal(struct testing s1, struct testing s2)
{ return (s1.a == s2.a && s1.b == s2.b && s1.c == s2.c); }
int main()
{ struct testing s1 = {44,4.4, 'A'};
struct testing s2 = s1;
s1.a = 55;
printf("%d\n",s1.a);
printf("%d\n",s2.a);
return 0;
}
Version 3:
Assume the player structure contains many data members. In this case, can we just use
structure variable to disp function? If we do so, this is considered as a bad practice. As
every member of argument is copied to every member of parameter, it requires more
space and more time to copy.
Also, when we are very sure that we do not want to make any changes to the argument,
parameter can be a pointer to constant structure
int main()
{
struct player p1={20,"sachin"};
printf("before change %s",p1.name);
p1=modify(p1);
printf("after change %s",p1.name);
return 0;
}
When the function modify() returns a changed parameter by value, it is copied to a temporary.
Then in the client code, the temporary is assigned to p1.
struct player modify(struct player p)
{
strcpy(p.name,"Sourav");
return p;
}
Usage of typedef
typedef is a keyword which is used to give a type, a new name. Used to assign alternative
names to existing data types. It is used to provide an interface for the client. Once a new name
is created using typedef, the client may use this without knowing the underlying type.
I want to store the age of a person. Best way to define a variable is int age = 80; Alternatively,
you can also say, typedef int age; //another name for int is age.
Then age a = 80;
Can we say age age = 80? // Think about this.
If we know how to declare a variable, then prefixing the declaration with typedef makes that a
new name.
int b[10]; // b is an array variable to store 10 integers
typedef int c[10]; // c is a name for an array of 10 integers
typedef is mostly used with user defined data types when names of the data types become
slightly longer and complicated to use in programs.
Version 1:
struct player
{ int id;
char name[20];
};
Version 2: You can include typedef when defining a new type itself.
typedef struct player
{ int id;
Nested Structures
Structure written inside another structure is called as nesting of two structures. It can bed
done in two ways.
1. Separate structures: Here, we create two structures, but the dependent structure should be
used inside the main structure as a member. Consider the following example.
struct dob
{
int date;
int month;
int year;
};
struct student // template declaration which describes how student entity looks like
{
int roll_no; // these three are members of the structure
char name[20];
int marks;
struct dob d1; //using structure variable of dob inside student
};
It can be understood from example that dob (date of birth) is the structure with members
as date, month and year. Dob is then used as a member in Student structure.
struct student // template declaration which describes how student entity looks like
{
int roll_no; // these three are members of the structure
char name[20];
int marks;
struct dob //declaring structure dob inside student
{
int date;
int month;
int year;
}d1;
};
Accessing Nested Structure Members: We can access the member of the nested structure by
using Outer_Structure.Nested_Structure.member as given below:
int main()
{
struct student s1 = {24, "John", 78, {20, 03, 2000}}; //Declaration and Initialization
printf("\n Roll no. = %d, Name= %s, Marks = %d, dob = %d.%d.%d ", s1.roll_no,
s1.name,s1.marks, s1.d1.date,s1.d1.month, s1.d1.year);
return 0;
}
Array of Structures
Consider,
struct student
{ int roll_no;
char name[20];
int marks;
};
Given the above user defined type for student entity, to store the details of one student, create
one variable of this structure type.
To store the details of 2 students, create two variables of this structure type.
To store the details of 100 students, there is a need to create 100 different structure variables.
As all the variables are of same type, we can use array here. So this can be done easily by making
use of Array of structures.
2. After structure declaration (either inside main or globally using struct keyword)
struct student s[100];
Initialization:
Involves Compile-time Initialization and Runtime Initialization.
Compile-time Initialization:
struct student S1[] = { {1, "John", 60}, {2,"Jack", 40}, {3, "Jill", 77} };
// size is decided by the compiler based on how many students details are stored
struct student S2[3] = { {1, "John", 60}, {2,"Jack", 40}, {3, "Jill", 77} };
// size is specified and initialized values are exactly matching with the size specified.
struct student S3[3] = {1, "John", 60, 2,"Jack", 40, 3, "Jill", 77};
//initialization can also be done this way
int main()
{ struct student s[100]; // 100 student variables created with the same name s.
// all members are initialized to undefined values in the beginning
//printf("%d---------\n",s[3].marks); // prints undefined value
int i;
for( i=0;i<3;i++)
{
printf("enter roll_num, name and marks\n");
scanf("%d",&s[i].roll_no); // observe . operator
scanf("%s",s[i].name);
fflush(stdin);
scanf("%d",&s[i].marks);
}
printf("details entered are --------- \n");
for(i=0;i<3;i++)
{ printf("roll_num, name and marks\n");
printf("%d\t",s[i].roll_no);
printf(“%s\t”, s[i].name);
printf("%d\n",s[i].marks);
}
return 0;
}
Pointer is used to access the array of structure variables efficiently. Suppose we have to
store record of 5 students, then using array of structure it can be easily done as:
struct student
{ int roll_no;
char name[22];
int marks;
}ST[5];
The sizeof operator when applied on structure student will take at least 30 bytes in memory. (Please
note this is implementation specific). Suppose, base address of the array ST is 1000, then pictorial
representation of the same in memory will be:
ST
1000
It means that pointer ptr is of type student structure and is holding the address pointed by the
array of structure ST (which is 1000).
ST
1000
Now, suppose if we perform ptr++, the ptr gets updated and now it will start pointing to next
array record starting from 1030 (ptr++ ptr+1 1000 + 30 = 1030), which is the record
of next student.
Example:
struct student ST[] = {{1, "John", 60}, {2, "Jack", 40}, {3, "Jill", 77}, {4, “Sam”, 78 }, {5,
“Dean”, 80}};
//prints 1 Jack 77
How we can use this ptr to print the details of all students??
int i;
for(i = 0; i < (sizeof(ST)/sizeof(ST[0])) ; i++)
printf("%d\t",(ptr+i)->roll_no); // ptr[i].roll_no
printf(“%s\t”, (ptr+i)->name);
printf("%d\n",(ptr+i)->marks);
}
Let us write the C code by creating a type called Book. The Book entity contains these data
members: id, title, author, price, year of publication. Write separate functions to read details of n
books and display it. Also include functions to do the following.
Fetch all the details of books published in the year entered by the user.
Fetch all the details of books whose author name is entered by the user. Display appropriate
message if no data found.
Separate the interface and implementation.
Let us begin with the header file. Header file contains the below code: book.h
typedef struct Book {
int id;
char title[100];
char author[100];
int price;
int year;
}book_t;
void read_details(book_t *b,int n);
void display_details(book_t *b, int n);
int fetch_books_year(book_t *b,int n, int year, book_t*);
int fetch_books_author(book_t *b,int n, char *author, book_t*);
book_t b[100];
book_t b_year[100];
book_t b_author[100];
printf("How many books details you want to enter?");
int n;
scanf("%d",&n);
printf("enter the details of %d books\n",n);
read_details(b,n);
int count;
printf("enter the year of publication to find list of books in that year");
int year;
scanf("%d",&year);
count = fetch_books_year(b,n, year,b_year);
if(count)
{
printf("List of books with %d as year of publication\n",year);
display_details(b_year, count);
}
else
printf("\n");
printf("enter the author name to find the list of books by that author\n");
char author[100];
scanf("%s",author);
count = fetch_books_author(b,n, author,b_author); if(count)
if (count)
{ printf("List of books by %s\n",author);
display_details(b_author, count);
}
else
printf("books by %s is not available in the dataset\n",author);
6 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Array of Structures
2021
Solution:
Client code provided here and I do not want to change the client at any cost. Adding the
the source files and header files as per this client only.
// employee_client.c
#include<stdio.h>
#include "employee.h"
int main()
{
employee_t e[1000];
printf("Enter the number of employees for which the details has to be entered\n");
int n, id;
scanf("%d", &n);
printf("enter name, id and basic salary of %d employees\n",n);
scan_details(e,n);
printf("enter the id of the employee whose total salary you want to know\n");
scanf("%d",&id);
int tot_salary = calculate_total_salary(e,n,id);
if (!tot_salary)
printf("employee with entered id doesnt exist\n");
else
printf("total salary of employee with id %d is %d\n",id,tot_salary);
printf("Employee details entered is as below\n");
display_details(e,n);
1 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Array of Structures: Practice Programs
2021
employee_t e_max = max(e,n);
printf("employee with maximum basic salary is %s",e_max.name);
return 0;
}
Program to read and display the details of n books using functions. Clearly separate interface
and implementation.
Program to display all information about a student who has scored highest marks in a class.
Consider inputs: 5 students (info.- name, roll_no, dob, marks scored in 5 different subjects)
Program to store the details of 5 flights. Also display all flights information sorted in order of
their arrival times.
Points to think:
In case of an array, memory is allocated before the execution time. Is there over
utilization or under utilization of memory?
Can we take the size of the array at runtime and request to allocate memory for the array
at runtime? – This is Variable length Array(VLA).
It is not a good idea to use this as there is no functionality in VLA to check
the non availability of the space. If the size of large, results in code crash without any
intimation.
Can we avoid this by allocating memory whenever and how much ever we require using
some of the functions?
malloc():
Allocates requested size of bytes and returns a void pointer pointing to the first byte of
the allocated space. Prototype: void *malloc(size_t size);
The malloc() function allocates size bytes and returns a pointer to the allocated
memory. The memory is not initialized. This returns a void pointer to the allocated memory
that is suitably aligned for any kind of variable. On error, these functions return NULL. NULL
may also be returned by a successful call to malloc() with a size of zero.
calloc():
Allocates space for elements, initialize them to zero and then return a void pointer to
the memory. prototype: void *calloc(size_t nmemb, size_t size);
The calloc() function allocates memory for an array of nmemb elements of size bytes each
and returns a pointer to the allocated memory. The calloc() function return a pointer to the
allocated memory that is suitably aligned for any kind of variable. On error, this function
return NULL. NULL may also be returned by a successful call to calloc() with nmemb or size
equal to zero
realloc():
Modifies the size of previously allocated space using above functions. copies the old
values into the new memory locations. Prototype: void *realloc(void *ptr, size_t size);
This function changes the size of the memory block pointed to by ptr to size bytes. The
content of the memory block is preserved up to the lesser of the new and old sizes, even if the
block is moved to a new location. If the new size is larger than the old size, the added memory
will not be initialized.
Note:
If ptr is NULL, then the call is equivalent to malloc(size), for all values of size.
If size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).
Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or
realloc(). If the area pointed to was moved, a free(ptr) is done. The realloc() function
returns a pointer to the newly allocated memory, which is suitably aligned for any kind of
variable and may be different from ptr, or NULL if the request fails. If size was equal to 0,
either NULL or a pointer suitable to be passed to free() is returned. If realloc() fails the
original block is left untouched, and it is not freed or moved
free(): Releases the allocated memory and returns it back to heap. Must be applied with
malloc(), calloc() or realloc()
Code 1:
int* p = (int*)malloc(sizeof(int)); //dynamic allocation for one integer
//address of that location is returned in p
*p = 10;
printf("p = %d", *p);
Code 2:
int* x;
int n, i;
printf("\n\nEnter number of elements:\n");
scanf("%d", &n);
else
{
printf("Memory successfully allocated using malloc.\n");
printf("Enter the integer values:\n ");
for (i = 0; i < n; ++i) // Scan the values
scanf("%d", x+i);
if (x == NULL)
printf("Memory not allocated.\n");
else
{
printf("Memory successfully allocated\n");
printf("The elements of the array before reading from the user\n");
for (i = 0; i < n; ++i)
{
printf("%d, ", *(x+i));
}
printf("\n Enter the elements:\n");
for (i = 0; i < n; ++i) //Scan elements of array
{
//scanf("%d", &x[i]);
scanf("%d", (x+i));
}
How does free function knows how much memory must be returned/released?
On allocation of Memory using memory management functions, it stores somewhere in
memory the # of bytes allocated. This is known as book keeping information. We cannot
access this info. But implementation has access to it. The function free finds this size given the
pointer returned by allocation functions and de-allocates the required amount of memory.
By observing both the diagrams above, we can get to know that block allocated using
functions will be deallocated and hence the pointer becomes dangling.
Dangling Pointer
• Points to a location which doesn’t exist
• Freeing the memory results in dangling pointer
• Using new pointer variable to store return address in realloc results in dangling
pointer
• Dereferencing the dangling pointer results in undefined behavior
• Can happen anywhere in the memory segment
• Solution is assigning the pointer to NULL
Version 2:
printf("%d\n",p3[i]); // *(p3+i)
Version3:
int *p4 = (int*)malloc(sizeof(int));
*p4 = 400;
printf(" %d\n", *p4); // 400
int *p5 = p4;
free(p5); // value of the pointer has the book keeping info available
Case 2: Using new pointer variable to store return address in realloc results in
dangling pointer
NULL Pointer
It is always a good practice to assign the pointer to NULL once after the usage of free on the
pointer to avoid dangling pointers. This results in NULL Pointer. Dereferencing the NULL
pointer results in Guaranteed crash.
Consider the below code.
*p1 = 100;
• If the same pointer is allocated memory more than once using the DMA functions,
initially allocated spaces becomes a garbage.
• Might corrupt the state of the memory manager that can cause existing blocks of
memory to get corrupted or future allocations to fail.
int* p = (int*)malloc(sizeof(int));
*p = 10;
printf("p = %d", *p);
free(p);
free(p);
The above might lead to undefined behavior. So, it is a good practice to make the pointer
NULL after the usage of free. By chance, if the pointer is freed again, it doesn’t do anything
with NULL.
free(p);
p = NULL; //Function free does nothing with a NULL pointer.
free(p);
INTRODUCTION
A linked list is a collection of nodes that are connected via links. The node and link has a special
meaning which we will be discussing in this chapter. Before we deal with this in detail, we need to
answer few questions.
Can we have pointer data member inside a structure? Yes. Refer to below codes.
Version 1:
struct Sample A
{
int a;
int *b;
};
int main()
{
struct A a1;
struct A a2;
a1.a = 100;
int c = 200;
a1.b = &c;
printf("a1 values:%d and %d\n", a1.a, *(a1.b)); // 100 200
a2 = a1;
printf("a2 values:%d and %d\n", a2.a, *(a2.b)); // 100 200 a1.a = 300;
*(a1.b) = 400;
printf("a2 values:%d and %d\n", a2.a, *(a2.b)); // 100 400
}
Version 2:
struct Sample
{
int a;
int *b;
};
#include<stdio.h>
int main()
{
struct Sample s;
s.a = 100;
s.b = &(s.a);
printf("%d %d",s.a,*(s.b));
struct Sample s1;
s1.a = 100;
s1.b = &(s1.a);
printf("%d %d\n",s1.a,*(s1.b));
struct Sample s2 = s1;
printf("%p %p\n",s1.b,s2.b);
printf("%d %d\n",s2.a,*(s2.b));
s2.a = 200;
printf("%p %p\n",s1.b,s2.b);
printf("%d %d\n",s1.a,*(s1.b));
printf("-----%d %d\n",s2.a,*(s2.b)); // very imp
*(s2.b) = 300;
printf("%p %p\n",s1.b,s2.b);
printf("%d %d\n",s1.a,*(s1.b));
printf("%d %d\n",s2.a,*(s2.b));
s2.b = &(s2.a);
*(s2.b) = 400;
printf("%p %p\n",s1.b,s2.b);
printf("%d %d\n",s1.a,*(s1.b));
printf("%d %d\n",s2.a,*(s2.b));
*/
}
Version 3:
int main()
{
struct A a1;
struct A a2;
a1.a = 100;
a1.b = (int*) malloc(sizeof(int));
*(a1.b) = 200;
printf("a1 values:%d and %d\n", a1.a, *(a1.b)); // 100 200
a2 = a1;
Version 4:
int main()
{
struct A a1;
struct A a2;
a1.a = 100;
};
Version 5 :
int main()
{
struct A a1;
a1.a = 100;
a1.b = (int*) malloc(sizeof(int));
*(a1.b) = 200; a1.p = NULL; // This creates garbage in heap
struct B
{
int a;
struct A a1;
}; // structure variable a1 in B
int main()
{
printf("sizeof A %d\n",sizeof(structA);
printf("sizeof B %d\n",sizeof(struct B));
}
The solution to the previous version is to have a pointer of the same type inside the structure.
The size of a pointer to any type is fixed. Hence, the size of the structure can be computed by the
compiler at compile time.
int main()
{
printf("%d\n",sizeof(struct Sample) ); // implementation specific struct Sample s;
s.a = 100; s.p = &s;
printf("%d %d %d\n", s.a, s.p->a, s.p->p->a); // all 100
return 0;
}
A data structure that consists of zero or more nodes. Every node is composed of two fields: a
data/component field and a pointer field. The pointer field of every node points to the next
node in the sequence.
We can access the nodes one after the other. There is no way to access the node directly as
random access is not possible in a linked list. Lists have sequential access.
int main()
{
NODE_T *s;
s = (NODE_T*) malloc(sizeof(NODE_T));
s->info = 100;
s->link = (NODE_T*) malloc(sizeof(NODE_T));
s->link->info = 200;
s->link->link = (NODE_T*) malloc(sizeof(NODE_T));
s->link->link-> info = 300;
Let us write functions to display this list and free every node in the list.
void display(NODE_T* q)
{
while(q != NULL)
{
printf("%d\t", q->info); q = q->link;
}
}
void freelist(NODE_T* q)
{
NODE_T* r; while(q != NULL){
printf("\n%d deleted",q->info);
r = q->link;
free(q);
q = r;
}
}
Ordered List
An ordered list has nodes arranged in the order of info field. Let us create a list of nodes in the
increasing order of info in every node.
First we will create empty list and initialize the list to NULL. As and when user enters elements,
make a node and insert this node to the list based on the order of the elements of the node. Some of
the conditions must be handled while inserting new node to the list. We will discuss the
implementations in detail.
#include<stdio.h>
int main()
{
MYLIST_T mylist;
//MYLIST_T *mylist;
initialize_list(&mylist);
printf("enter the number of elements u want to insert\n");
int n,ele;
scanf("%d",&n);
for(int i = 0;i<n; i++)
{
printf("enter the element-->"); scanf("%d",&ele);
insert_list(&mylist,ele);
}
printf("Elements of the list are\n"); display_list(&mylist);
free_list(&mylist);
}
Server Side :
There are four cases to be considered while inserting node to the ordered list:
1. empty list
2. insert in the middle
3. insert at the beginning
4. insert at the end
Insert_list: The insert_list function creates a node from the element entered by the user and makes
the link field of the node to NULL.
}
printf("\n");
}
{
NODE_T* p = p_list->head;
NODE_T* r;
while(p!=NULL)
{
r = p;
p = p->link;
free(r);
printf("\nnode is freed");
}
}
Requirements:
1. Creation of two new user defined types: date and event
typedef struct date { int dd; int mm; int yy; } date_t;
typedef struct event { char event_name[10]; date_t date; } event_t;
2. Add functions to read and display the date. Use these functions to read and display the
event details.
3. Add is_month_matching and is_date_in_order functions in date. Use these functions to
check whether the event is in the particular month and whether the event is in order or
not(consider either date or month).
4. Add functions to read and display an array of events. Also add a function to find the
count of events in that month.
};
typedef struct date date_t;
void read(date_t*);
void disp(const date_t*);
int is_month_matching(const date_t *,int);
int is_date_in_order(const date_t *,const date_t *);
// event.h as below
#include"date.h"
struct event
{
char event_name[10];
date_t event_date;
};
typedef struct event event_t;
void read_event(event_t*);
void display_event(const event_t*);
int is_event_in_month(const event_t*,int);
int is_event_in_order(const event_t* lhs,const event_t* rhs);
// event_array.h as below
#include"event.h"
void read_event_array(event_t*,int);
void display_event_array(const event_t*,int);
int count_of_events_in_month(const event_t[],int,int);
printf("If you want to know the count of events in paarticular month,enter the month
number between 1 and 12\n");
scanf("%d",&mon);
if (mon<1 || mon>12)
printf("this month number is invalid\n");
else
printf("number of events in month number %d are
%d\n",mon,count_of_events_in_month(e,n,mon));
int i,j;
printf("enter two event numbers to check whether they are in order\n");
scanf("%d %d",&i,&j);
if(i>=0 && i<n && j>=0 && j<n)
{
if(is_event_in_order(&e[i],&e[j]))
printf("in order\n");
else
3 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Problem Solving: Scheduling
2021
printf("Not in order\n");
}
else
{
printf("entered event number is not valid");
}
printf("THANK YOU\n");
return 0;
}
Note: Compile the client code and keep the object file ready. Do not touch this again and again.
Let us start with adding the implementations for all the functions available in header files.
//date.c
#include<stdio.h>
#include"date.h" // observe why it is added here?
void read(date_t* d)
{
scanf("%d %d %d",&(d->dd),&(d->mm),&(d->yy));
}
void disp(const date_t* d)
{
printf("%d/%d/%d\n",d->dd,d->mm,d->yy);
}
int is_month_matching(const date_t *d,int month)
{
return (d->mm == month);
}
int is_date_in_order(const date_t *lhs,const date_t *rhs)
{
return lhs->dd < rhs->dd;
}
// event.c
#include<stdio.h>
#include"event.h" // observe this
void read_event(event_t* e)
{
printf("enter the name of the event\n");
scanf("%s",e->event_name);
printf("enter the date\n");
read(&(e->event_date));
}
void display_event(const event_t* e)
{
printf("%s\t",e->event_name);
disp(&(e->event_date));
}
int is_event_in_month(const event_t* e,int month)
{
return is_month_matching(&(e->event_date),month);
}
int is_event_in_order(const event_t* e1,const event_t* e2)
{
return is_date_in_order(&(e1->event_date),&(e2->event_date));
}
//event_array.c
#include"event_array.h"
void read_event_array(event_t *e,int n)
{ int i;
for(i = 0; i< n; i++)
{
read_event(&(e[i])); // e+i is pointer notation
}
}
5 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Problem Solving: Scheduling
2021
Note: Compile each of the above source files separately. And then link 4 object files to get the
executable.
gcc -c date.c
gcc -c event.c
gcc -c event_array.c
gcc event_client.o event.o event_array.o date.o -o event.exe // renaming the executable. Not
a requirement though
Then run event.exe to get the solution
As there are multiple files involved in the solution for the problem, it is good to write make file and
use make command so that the developer need not remember every now and then, to which file
changes are made.
//event.mk is as below
event.exe : event_client.o event.o event_array.o date.o
gcc event_client.o event.o event_array.o date.o -o event.exe
event_client.o: event_client.c event_array.h
gcc -c event_client.c
event.o:event.c event.h
gcc -c event.c
event_array.o : event_array.c event_array.h
gcc -c event_array.c
date.o: date.c date.h
gcc -c date.c
Execution:
In windows OS, mingw32-make –f event.mk and press enter. Then run the executable
event.exe.
In ubuntu OS, small changes must be done in the make file. First two lines in the make file
are as below. Others remain the same
event.out : event_client.o event.o event_array.o date.o
gcc event_client.o event.o event_array.o date.o -o event.out
Command used to run the make file: make –f event.mk and press enter. Then run the
event.out using ./event.out
Queue:
A line or a sequence of people or vehicles waiting for their turn to be attended or to proceed.
In computer Science, a list of data items, commands, etc., stored so as to be retrievable in a
definite order. A Non- Linear data structure which has 2 ends - Rear end and a Front end.
Data elements are inserted into the queue from the rear(back) end and deleted from the front end.
Hence a queue is also known as First In First Out (FIFO) data structure.
peek() − Gets the element at the front of the queue without removing it.
isfull() − Checks if the queue is full.
isempty() − Checks if the queue is empty.
Types of queue:
Ordinary queue - insertion takes place at rear and deletion takes place at front.
Priority queue - special type of queue in which each element is associated with a priority
and is served according to its priority. If elements with the same priority occur, they are
served according to their order in the queue
Circular queue - Last element points to first element of queue making circular link.
Double ended queue - insertion and removal of elements can be performed from either from
the front or rear.
Priority Queue
A data structure which is like a regular queue but where additionally each element has a
"priority" associated with it. This priority decides about the deque operation. The enqueue
operation stores the item and the priority information.
Types of Priority Queue:
Ascending priority queue:
The smallest number, the highest priority.
Descending priority queue:
The highest number, the highest priority.
Let us consider the implementation of Descending Priority Queue using an Unordered Linked
list.
#include<stdio.h>
#include “priority.h”
int main()
{
prio_t queue;
init_queue(&queue);
compo_t c;
printf("enter ua choice\n1.insert\n2.delete\n3.disp\n4.exit\n");
int choice;
scanf("%d",&choice);
do
Then based on the choice entered by the user, enqueue, dequeue and display happens. When the
choice is 1, the user is allowed to give defined values for component structure: details and
priority. The en queue takes priority queue and component as arguments. When the choice is 2,
de queue takes priority queue and stores the component to be deleted based on priority in a local
variable and frees the node to be deleted. When choice is 3, display is called which is self-
explanatory. These are repeated in a looping structure.
void enque(prio_t* p_q,compo_t* com) // This function creates a node using the component
from the client and adds this node in the beginning of the queue.
{
temp->c.priority = com->priority;
//temp->link = NULL;
void deque(prio_t* p_q) // used to delete the node from the queue based on the priority
{
if(p_q->head == NULL)
printf("No elements in the Q to delete\n");
else
{
node_t *present = p_q ->head; // present points to wherever head is pointing
node_t *prev = NULL;
// prev points to NULL initially. But later one before the present node
int max = 0;
node_t *prev_max = NULL; // link field of this points to the node with highest
priority in the component.
while(present != NULL)
{
6 Department of Computer Science & Engineering(Problem Solving With C) PESU
Unit III : Problem Solving: Priority Queue Implementation
2021
if(present->c.priority >= max)
{
max = present->c.priority;
prev_max = prev;
}
prev = present;
present = present->link;
}
compo_t compo;// to store the details of removed node. Just for printing purpose
if(prev_max != NULL)
{
//Remove in the middle or the end of the list. The head will not change.
node_t *temp = prev_max->link;
prev_max->link = temp->link;
//copy the node information to component before deleting the node
strcpy(compo.details,temp->c.details);
compo.priority = temp->c.priority;
free(temp);
}
else
{
// if the first node has the highest priority, head must change
node_t *temp = p_q ->head;
p_q->head = p_q ->head>link;
void disp(const prio_t* p_q) // display the nodes, in turn components of the priority queue
{
node_t *p = p_q ->head;
if(p == NULL)
{
printf("No elements in queue");
}
else
{
while(p != NULL)
{
printf("%s %d\n",p->c.details,p->c.priority);
p = p->link;
}
}
}
Segmentation Fault
When a program runs, it has access to certain portions of memory. It may have some local variables
in every function. These are stored in the Stack. May have some memory allocated during runtime.
These are allocated on the Heap. Program can only touch the memory that belongs to it - the
memory previously mentioned. Any access outside this area will cause a segmentation fault.
Segmentation faults are commonly referred to as segfaults.
They are caused by a program trying to read or write an illegal memory location. A common
condition that causes programs to crash.
An error returned by hardware with memory protection that tells the Operating System(OS)
that memory violation has occurred
The OS usually reacts by telling the offending process about the error through a signal and
then it performs some sort of corrective action
Example scenarios
When scanf unable to perform write operation
Performing write operation on a read only location
Performing read or write operation on freed block of memory once it is NULL
Example code 2:
#include<stdio.h>
int main()
{
char* p = "pes";
printf("before change %s\n",p);
p[1] = 'E';
printf("after change %s\n",p);
return 0;
}
When you pass the executable of above code to gdb, here is the snapshot. Observe the signal,
SIGSEGV.
Example code 3:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int));
*p = 200;
printf("before free p points to %d\n",*p) ;
free(p);
printf("after free p points to %d\n",*p); //undefined behavior. So might crash here too
p = NULL;
printf("after p is NULL, it points to %d\n",*p);// code crash for sure
return 0;
}
When you pass the executable of above code to gdb, here is the snapshot. Observe the signal,
SIGSEGV.
Memory Leak
Location with no name and hence no access
If free is not used on the pointer after allocating memory on the heap, it becomes a
garbage if pointer is pointing to a new memory location resulting in memory leak.
p = (int*)malloc(sizeof(int));
*p = 300;
printf("Then p is pointing to %d\n",*p);
p = (int*)malloc(sizeof(int));
*p = 400;
printf("Then p is pointing to %d\n",*p);
The memory which is allocated on the heap is not freed and the same pointer the made to point to
different locations on the heap. This code creates memory leak.
Let us see how GDB finds memory leak in an executable.
This is not supported by all versions of GDB
Valgrind is used in Linux to locate memory leaks
info leaks and info heap are commands that are available in gdb only on HP-UX
ptype: Prints the description of data type for a given variable. Differs from whatis by
printing a detailed description, instead of just the name of the type.