0% found this document useful (0 votes)
2 views106 pages

DS +apr 2021-2022+

The document outlines a course on Advanced C & Data Structures, detailing prerequisites, session topics, and key concepts such as pointers, recursion, structures, file I/O, and various data structures like stacks and linked lists. It emphasizes the importance of pointers in C programming, explaining their declaration, arithmetic, and usage in function arguments. Additionally, it covers advanced pointer topics, including null pointers and the use of the const keyword in pointer declarations.

Uploaded by

B. Sandeep
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)
2 views106 pages

DS +apr 2021-2022+

The document outlines a course on Advanced C & Data Structures, detailing prerequisites, session topics, and key concepts such as pointers, recursion, structures, file I/O, and various data structures like stacks and linked lists. It emphasizes the importance of pointers in C programming, explaining their declaration, arithmetic, and usage in function arguments. Additionally, it covers advanced pointer topics, including null pointers and the use of the const keyword in pointer declarations.

Uploaded by

B. Sandeep
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/ 106

Advanced C & Data Structure www.cranesvarsity.

com

Advanced C & Data Structure

Prerequisite It is advised that the participants revise the topics of Programming in C Module.

© Cranes Varsity V2.1-2017-18 1/ 106


Advanced C & Data Structure www.cranesvarsity.com

Module Advanced C & Data Structure


Session Lab -
Sl.No Topics / Subtopics Page #
# Y/N
1 Introduction to Pointers 2
What Is a Pointer? 4
Pointer Declaration 5
Pointer Arithematic Operation 5
Relation between Pointers and Arrays 6
Advanced Pointers : NULL pointer, Pointer to a constant , constant
2 1 Y 8
pointer
Pointer to Array & Array of Pointers 1 10
Function pointer & Void pointer 1 13
Dynamic Memory Allocation 1 19
Memory Leaks : Valgrind 21
Command line arguments 1 24
Converting String to integer using atoi 25
3 Recursion 1 Y
Recursion : how it works? 27
Single branch recursion: Finding the Factorial using recursion 28
Multiple branch recursion: print Fibonacci series using recursion 29
4 Structure 2 Y
Declaration of Structure and Structure Variable 31
Memory allocation Concept and accessing Structure Members 32
Passing Structure to Function by value / Address 33
Array of Structure 34
Nested Structure 35
Self referential Structure 37
Bitfield 1 39
Union 40
Enum 42
Typedef 43
Structure padding using #pragma 44
5 Files I/O 1 Y
Text, Binary Files 47
Opening, closing files 48
Character I/O - fgetc, fputc 50
String I/O - fgets, fputs 52
String I/O-sscanf,sprintf 55

© Cranes Varsity V2.1-2017-18 2/ 106


Advanced C & Data Structure www.cranesvarsity.com

Block I/O - fread and fwrite 56


Random Access - fseek , ftell , rewind 57
6 Data structures Y
Stack, Applications of Stack 1 63
Queue, Applications of Queue 76
Linked List
Single Linked List : Ins Front, Rear, Order ; Del Front, Rear , key 1 78
Double Linked List : Traversal Front-Rear & Rear - Front 1 88
Circular Linked List 1 93
Trees 95
Trees - BST : Insertion, Traversal, Height, Count Leaf 1 95
Expression Tree 1 103
Total 18 sessions

© Cranes Varsity V2.1-2017-18 3/ 106


Advanced C & Data Structure www.cranesvarsity.com

CHAPTER – 1
POINTERS

What is a pointer?
A pointer is a variable which holds the address of another variable. The value contained by a pointer must
be an address that indicates the location of another variable in the memory. That's why a pointer is also
called an address variable.

Why have Pointers?


Pointers solve two common software problems.
 Pointers allow different sections of code to share information easily. You can get the
same effect by copying information back and forth, but pointers solve the problem better.
 Pointers enable complex data structures like linked lists and binary trees.
Pointer Declarations
The syntax for declaring pointers is like that for variables, except that pointers are distinguished by an
asterisk that prefixes their names. ‘*’ is called value at address operator. It is also called as indirection
operator. Hence if it is required to store an address of an integer use the following declaration

int *ptr;

This declaration tells the compiler that ptr will be used to store the address of an integer. meaning the
value at the address contained in ptr is an int.

short *pt1; /*pt1 is a pointer to a short integer */


char *pt2; /*pt2 is a pointer to a character */
long *pt3; /*pt3 is a pointer to a long integer */
unsigned short data ,*pt4; /*pt4 is a pointer to unsigned
short and data is a normal variable of type unsigned short int*/

Note: Size of every type of pointer is same.


Declaring and assigning values to pointers.

#include <stdio.h>
int main( )
{
int x, *ptr_x;
x = 7;
printf("x: address=%p, content=%d\n", &x, x);
ptr_x = &x;
printf("ptr_x: address=%p, content=%p\n", \
&ptr_x, ptr_x);
printf("*ptr_x => %d\n", *ptr_x);
return 0;
}

Note: The pointer type and the memory that it refers should be same. It is necessary that we have to
create an integer pointer to hold the address of type integer memory.
Example.
char char_var = ‘p’;
© Cranes Varsity V2.1-2017-18 4/ 106
Advanced C & Data Structure www.cranesvarsity.com

int num= 10, *ptr;


ptr = &num; /* Valid */
ptr = &char_var; /* Invalid */

Pointer dereferencing
We can use the pointer to retrieve data from memory or to store data into the memory to which it points
to. Both operations are classified as pointer dereferencing. It is also called as indirection operator(‘ * ’) .
int var=10, var1, *ptr;
ptr = &var;
var1= *ptr;
In the above the first line is defining two normal variables var and var1 where as ptr is a pointer type of
variable. In the second line ptr pointer, holds the address of var, and `var1 = *ptr' copies the content of
var into var1 using pointer.
Consider the following
char ch; /* a character */
char *pch; /* a pointer to a character */
char **ppch; /*a pointer to a pointer to a character */
ch = ‘A’;
pch = &ch;
ppch = &pch;

Here we can see that ch variable’s address is stored as the content of pch and the address of pch is
stored as the content of ppch.
 The contents of ch normal variable is, the ASCII value of ‘A’
 The contents of pch pointer is, the address of ch variable (pch = &ch)
 By single indirection (dereferencing) gives the char data (*pch  ‘A’)
 The contents of ppch pointer is, the address of pch (ppch = &pch)
 By single indirection (dereferencing) gives the address of the pointer (*ppch  &ch)
 By double indirection (dereferencing) gives the char data (**ppch  ‘A’)

Pointer Arithmetic: The permissible pointer operations


 A pointer can have the address of an ordinary variable as its content (e.g., pv=&v)
 The content of a pointer can be copied to another pointer variable provided both pointers
are of same type. (e.g., pv = px)
 A pointer variable can be assigned a null(zero) value. (e.g., pv=NULL, where NULL is a
symbolic constant that represents the value 0).
 An integer quantity can be added to or subtracted from a pointer variable. (e.g., pv+3,
++pv, pv – 2, pv--, etc.).
 Pointers can be subtracted if the pointers are of same type. (e.g., pv-px), pointers can be
compared using relational operators (e.g., pv>px, pv==py, pv != py, etc.).

The pointer operations (not-permissible)


 Two pointer variables cannot be added. (e.g., pv+px)
© Cranes Varsity V2.1-2017-18 5/ 106
Advanced C & Data Structure www.cranesvarsity.com

 Pointer variable cannot be multiplied by a constant. (e.g., pv*3).


 Two pointer variables cannot be divided or multiplied. (e.g., pv * px, px/py)
Arguments to Functions
Arguments are passed to functions in two ways.
Pass by value
Pass by address.

Passing Arguments by Value


C passes parameters "by value" which means that the actual parameter values are copied into local
storage. The caller and callee functions do not share any memory – each one has its own copy.

The parameter is initialized with the value of the argument. You can change the value of the parameter (if
that parameter has not been declared const) within the scope of the function, but these changes will not
affect the value of the argument in the calling function.

Passing Arguments by Address


The alternative to pass by value is passing by address. In this, instead of passing a copy of a value from
the calling function to the called function, we pass the address of the variable as argument. In this way
the called function accesses the actual argument through pointers.
Eg: Pass by address

#include <stdio.h>
void increment (int *x);
int main ( )
{

int count = 5;

/* address of count is passed to the function */


increment (&count) ;
printf("count = %d\n", count);
return (0);
}
void increment (int *x)
{
*x = *x + 1;
printf("*x = %d\n", *x);
}

The Pointer and Array

Pointers and arrays are closely related; Any operation that can be achieved by array subscripting can
also be done with pointers. The pointer version will in general be faster
Mentioning the name of array gives the base address of an array;

Consider the following:


int arr[SIZE];
int *pa;
pa = arr; // is same as pa = &arr[0];
© Cranes Varsity V2.1-2017-18 6/ 106
Advanced C & Data Structure www.cranesvarsity.com

If pa points to a particular element of an array, then by definition pa+1 points to the next
element, pa+i points i elements after pa, and pa- i points i elements before. Thus, if pa points to a[0],
*(pa+1) refers to the contents of a[1], pa+i is the address of a[i], and *(pa+i) is the contents of a[i].

These remarks are true regardless of the type or size of the variables in the array a. The meaning of
“adding 1 to a pointer”, and by extension, all pointer arithmetic, is that pa+1 points to the next object, and
pa+i points to the ‘iy’ th object beyond pa. a reference to a[i] can also be written as *(a+i). In evaluating
a[i], C converts it to *(a+i) immediately; the two forms are equivalent. Applying the operator & to both
parts of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the address of the iþ th
element beyond a. As the other side of this coin, if pa is a pointer, expressions might use it with a
subscript; pa[i] is identical to *(pa+i). In short, an arrayþ andþ index expression is equivalent to one
written as a pointer and offset.

There is one difference between an array name and a pointer that must be kept in mind. A pointer is a
variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and
a++ are illegal.

When an array name is passed to a function, what is passed is the location of the initial element. Within
the called function, this argument is a local variable, and so an array name parameter is a pointer, that is,
a variable containing an address.

Program to find the length of the string /* strlen: return length of string s */
int strlen(const char *s)
{
char *p = s;
while(*s++)
;
return s – p;
}

Since s is a pointer, incrementing it is perfectly legal; s++ has no effect on the character string in the
function that called strlen, but merely increments strlen's private copy of the pointer. That means that calls
like all work

strlen("hello, world"); /* string constant */


strlen(array); /* char array[100]; */
strlen(ptr); /* char *ptr; */

As formal parameters in a function definition,


char s[ ]; and
char *s; are equivalent;

© Cranes Varsity V2.1-2017-18 7/ 106


Advanced C & Data Structure www.cranesvarsity.com

CHAPTER 2
Advanced pointers
NULL Pointers

A pointer is a variable which can hold the address of another variable. There is one other value a pointer
may have: NULL value, then it is called as null pointer.
A null pointer is a special pointer not pointing to any variable or array cell which are usually created either
in Data Segment or in Stack segment, and also the null pointer is not pointing to any valid memory in the
heap.
The null pointer will hold NULL value as its content.
int *ip = NULL;
It is also possible to refer to the null pointer by using a constant 0, and you will see some code that sets
null pointers by simply doing
int *ip = 0;
Above said is legal, but the recommended one is using constant NULL for clarity.
The macro NULL is defined in stdlib.h, stdio.h and other header files as a null pointer constant.

A null pointer is always unequal to any valid pointer to an object or function. For this reason, functions
that return a pointer type usually use a null pointer to indicate a failure condition. One example is the
standard function malloc( ), which returns a null pointer if it fails to allocate the memory:
#include <stdio.h>
/* ... */
int *ptr = malloc(20);
if (ptr == NULL)
{
// Error: unable to allocate memory
}

Null pointers are implicitly converted to other pointer types as necessary for assignment operations, or for
comparisons using == or !=. Hence no cast operator is necessary in the previous example.

Using const with Pointers and Parameters Declarations


Using the const keyword while declaring a simple variable and an array, is pretty easy. Pointers are more
complicated because you have to distinguish between making the pointer itself const(constant pointer)
and making the value is pointed to const(pointer to constant).

The declaration
const float *pf; /* pf is a pointer to float constant */

establishes that pf points to value, that must remain constant. The value of pf itself can be changed. For
example, it can be set to point at another variable. In contrast, the declaration

float *const pt; /* pt is a const pointer to float */


says that the pointer pt itself cannot have its value changed. It must always point to the same address,
but the pointed to value can change. Finally, the declaration
const float * const ptr; /* ptr is a constant pointer to float constant */
means ptr must always point to the same location and that the value stored at the location must not
change. There is third location in which you can place const:
© Cranes Varsity V2.1-2017-18 8/ 106
Advanced C & Data Structure www.cranesvarsity.com

float const *pfc; /* same as const float * pfc; */


As the comment indicates, placing const after the type name and before the * means the pointer can’t be
used to change the pointer-to value. In short, a const anywhere to the left of the *, makes the data
constant and a const to the right of the *, makes the pointer itself constant. One common use for const is
declaring pointers that serve as formal function parameters. For example, suppose you have a function
called display( ) that displays the contents of an array.

To use it, you would pass the name of the array as an actual argument, but the name of an array is an
address, that would enable the function to alter data in the calling function. But the following prototype
prevents this from happening

void display(const int array[ ],int limit);

In a prototype and a function header, the parameter declaration const int array[ ] is the same as const int
* array, so the declaration says that the data to which array points cannot be changed.

The ANSI C library follows this practice. If a pointer is used only to give a function access to values, the
pointer is declared as a pointer to a const-qualifier type. If the pointer is used to alter data in the calling
function, the const keyword isn’t used. For example, the ANSI C declaration for strcat( ) is this:

char *strcat(char *,const char *);


Recall that strcat( ) adds a copy of the second string to the end of the first string. This modifies the first
string, but leaves the second string unchanged. The declaration reflects this.

Pointer to string literal

char *ptr = “CRANES VARSITY”;


The above statement is perfectly valid.

What is the size of ptr?


Ans : 4 bytes

How does a pointer of 4 bytes hold the string?


The pointer does not hold the string. The string is stored at different location and pointer points to /holds
the starting address of string.

ptr CRANES VARSITY

There is an important difference between these definitions:


char str[] = "Cranes"; /* an array */
char *ptr = "Cranes"; /* a pointer */

str is an array, just big enough to hold the sequence of characters and'\0' that initializes it. Individual
characters within the array may be changed but str will always refer to the same storage.

On the other hand, ptr is a pointer, initialized to point to a string constant; the pointer may subsequently
be modified to point elsewhere, but the result is undefined if you try to modify the string contents.
© Cranes Varsity V2.1-2017-18 9/ 106
Advanced C & Data Structure www.cranesvarsity.com

/******************** program.c ***************/


int main()
{
char *ptr = “String1” // This is a string literal allocated memory in read-only section
char str[ ] =“String2” //This is an array of character where memory will be allocated in stack segment
ptr[ 0 ] = ‘X’; // Not allowed . it is undefined behavior
str [ 1 ] = ‘Y’; // Valid

return 0;
}
You can look at string literal as "a sequence of characters surrounded by double quotes". This string is
stored at read-only memory and trying to modify this memory leads to undefined behavior (usually
segmentation fault).

So how come that you get segmentation fault?


The main point is that char *ptr = "string1" makes ptr to point to the read-only memory where your string
literal is stored. So when you try to access this memory: ptr[0] = 'X'(which is by
the way equivalent to *(ptr + 0) = 'X'), it is a memory access violation.
On the other hand: char b[] = "string2"; allocates memory and copies string "string2" into it, thus modifying
it is valid. This memory is freed when b goes out of scope.

Array of Pointers
The way there can be an array of integers or an array of floats, similarly there can be an array of pointers.
Since a pointer variable always contains an address, an array of pointer would be nothing but a collection
of address. All rules that apply to an ordinary array applied to the array of pointers as well.

#include <stdio.h>
#include <string.h>
void print_long (char **cities,int size);
int main( )
{
int a = 10, b = 20, c = 30, I;
char *ptr = “Bangalore”;
char str[ ] = “Mumbai”;
char *cities [ ] = {“Delhi”, ”Hyderabad”, ptr, str};
print_long(cities, sizeof(cities) / sizeof(char *));
return 0;
}
void print_long (char **cities,int size)
{
int longindex, i;
longindex = 0;
for(i = 1 ; i < size ; i++)
{
if(strlen(cities[i]) > strlen(cities[longindex]))
longindex = i;
}
printf("\n %s \n",cities[longindex]);
© Cranes Varsity V2.1-2017-18 10/ 106
Advanced C & Data Structure www.cranesvarsity.com

Array of Pointers vs. Multi dimensional Arrays

Newcomers to C are sometimes confused about the difference between a two dimensional array and an
array of pointers. Given the definitions
int a[10][20];
int *b[10];

then a[3][4] and b[3][4] are both syntactically legal references to a single int. But a is a true two"
dimensional array: 200 int" sized locations have been set aside.

For b, however, the definition only allocates 10 pointers and does not initialize them; initialization must be
done explicitly, either statically or with code.

Arrays of Strings Implementation

 We could implement an array of strings as a 2-D array of chars


o char array[10][10];
 This has two disadvantages
o All strings will be 10 chars long
o Requires 2 nested for-loops for most operations such as string comparison or string
copying, which can become complicated
 Instead, we will implement our array of strings as an array of pointers
o char *array[10];
 Each pointer points to one string
o Follow the string through the pointer
o Go to the next string using a for-loop
o Because strcpy, strcmp, strlen all expect pointers, we can use these by passing an array
element (since each array element is a pointer to a string)

Example
char *p[ ] = {"hello", "goodbye", "so long", "thanks for all the help"};
// p is an array of 4 pointers
char *y; // let y be a pointer to a char so it can be used to move through a single string
int i;
for(i=0;i<4;i++) // iterate for each string in x
{
puts(p[i]);
}

Notice that if we had used char x[ ][ ] = {…}; then the storage space would have been 4 strings of
length 24 (the length of the longest string) or 96 bytes.In the above code memory used is 16 bytes for
pointer + 46(6 + 8 + 8 + 24) = 62.

© Cranes Varsity V2.1-2017-18 11/ 106


Advanced C & Data Structure www.cranesvarsity.com

/*Program to store 4 names in your Program and sort it in alphabetical order using array
of pointers*/

int main()
{
char *p[4]={" Swetha"," Abhishek"," Satish"," Kunal"};
char *temp;
int i,j;
printf("Before sorting: names are\n");
for(i=0;i<4;i++)
{
puts(p[i]);
}
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
if(strcmp(p[j],p[j+1])>0)
{
temp=p[j];
p[j]=p[j+1];
p[j+1]=temp;

}
}

}
printf("After sorting: names are\n");
for(i=0;i<4;i++)
{
puts(p[i]);
}
}

Diagram
p

p[0] char * Swetha

p[1] char * Abhishek

char * Satish
p[2]
char * Kunal
p[3]

Here, p is an array of 4 character pointers. The size of p is 16 bytes , whereas the size of p[0] is 4 bytes.
p[0] is a pointer which points to the first string of the array .

© Cranes Varsity V2.1-2017-18 12/ 106


Advanced C & Data Structure www.cranesvarsity.com

Pointer to pointer

We can have pointers to int,char, float and in fact to any datatype , pointer to a pointer can also be
created. Consider,

int i = 5;

int *ptr = &i;

Here, ptr is a pointer to integer i.

A pointer which is capable of holding address of ptr should be of type int **

int **pptr = &ptr;

pptr ptr i
When we pass an array, its base address (address of the first element) will be passed and hence the
pointer of the same type should be used as the function parameter. Any modification made to that array in
the function will get reflected back.
Consider p is an array of 4 character pointers. If we want to declare a pointer which should point to this
array of pointers p, it should be of type char ** . That is,
char *p[ ] ={" Sikander","Naveen","Monica","Zubair"};
char **cptr = p;

cptr is a pointer-to-pointer to character.

char * Sikander
Swetha

char * Abhishek
Naveen

char * Satish
Monica

char * Zubair
Kunal

Pointer to a function

Function pointers in C are a powerful tool in embedded programming. A function pointer is a variable
which holds the memory location of a function and can point to different functions with the same return
and parameter declarations. Two great uses are function callbacks and jump tables.

A pointer to a function points to the address of the function which is in Code Segment. We can use
pointers to function, to call functions, to pass function as an argument to other functions and return a

© Cranes Varsity V2.1-2017-18 13/ 106


Advanced C & Data Structure www.cranesvarsity.com

function’s address as the return value. But we cannot perform pointer arithmetic operations on pointers to
functions.

The type of a pointer to a function is based on both the return type and parameter types of the function. A
declaration of a pointer to a function must have the pointer name in parentheses. The function call
operator ( ) has a higher precedence than the dereference operator *. Without them, the compiler
interprets the statement as a function that returns a pointer to a specified return type. For example:

int *f (void); /* function f returning an int* */


int (*g) (void); /* pointer to a function (g) returning an int */
char (*h) (int, int) /* h is a pointer to function that can hold the address of a function
which takes two integer parameters and returns char */

In the first declaration, f is interpreted as a function that takes an int as argument, and returns a pointer to
an int. In the second declaration, g is interpreted as a pointer to a function that takes an int argument and
that returns an int.

Example
#include <stdio.h>
#include <string.h>

int add(int x,int y)


{
printf(__func__);
return x + y;
}
int sub(int x,int y)
{
printf(__func__);
return x - y;
}
int mul(int x,int y)
{
printf(__func__);
return x * y;
}
int div(int x,int y)
{
printf(__func__);
return x / y;
}
int main( )
{
int a,b,ch,res;
int (*p)(int,int);

//returntype (*p)([argument list]);

printf("\n ENTER TWO NUMBERS : ");


scanf("%d %d",&a,&b);
© Cranes Varsity V2.1-2017-18 14/ 106
Advanced C & Data Structure www.cranesvarsity.com

printf("\n 1. ADD 2. SUB 3. MUL 4. DIV ");


scanf("%d",&ch);
switch(ch)
{
case 1 : p = add; break;
case 2 : p = sub; break;
case 3 : p = mul; break;
case 4 : p = div; break;
}
res = p(a,b);
printf("\n RES = %d",res);
}

Callback Function

A useful function pointer application is a function callback. This is where a function pointer is passed as a
parameter to a function. As an example, let's say a function reads data from a file but then does not know
what to do when the data arrives. The programmer can use a callback function that writes the data
somewhere as it arrives.
#include <stdio.h>
#include <stdlib.h>
void my_write_func(char * str){
printf("received:%s", str);
}

void my_discard_func(char * str){


//don't output the data
printf("Data is discarded\n");
}
void my_read_func(void (*callback)(char *str)){
char buffer[64];
if( fgets(buffer, 64, stdin) != NULL ){
callback(buffer);
}
}
int main(int argc, char * argv[]){
printf("Read from standard input\n");
my_read_func(my_write_func);
my_read_func(my_discard_func);
return 0;
}

Array of Function Pointers

Function pointers can be used in applications where we don’t know in advance which function wil be
called. Consider a program that has three inputs: 1, 2, and 3. For each input, a different function is
executed. This can easily be done with a switch statement, but it is easier to use an array of function
pointers, if the number of inputs is much larger. The program below shows both approached.

© Cranes Varsity V2.1-2017-18 15/ 106


Advanced C & Data Structure www.cranesvarsity.com

int main()
{
int (*pf[4])(int,int) = {add,sub,mul,div};
int a,b,ch;
printf("\n ENTER TWO NUMBERS : ");
scanf("%d %d",&a,&b);
printf("\n 1. ADD 2. SUB 3. MAX 4. MIN: ");
scanf("%d",&ch);
printf("\n The result = %d ",(*pf[ch-1])(a,b));
return 0;
}

Call back example Qsort

The standard function qsort takes a pointer to a comparison function as one of its arguments, in addition
to the information about the array to be sorted. qsort uses the pointer to call the specified function
whenever it has to compare two array elements.

Prototype of Qsort:

void qsort(void *base, size_t num, size_t width, int (__cdecl *compare)(const void *elem1, const void
*elem2));

Program to demonstrate the use of function pointer passed as argument using qsort( ) library function

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

#define N 5
int compareInt(const void *a,const void *b)
{
return *(int *)a - *(int *)b;
}
int compareString(const void *a,const void *b)
{
return strcmp(a,b);
}
void displayInt(int arr[ ],int n)
{
int i;
for(i = 0 ; i < n ; i++)
printf(" %d ",arr[i]);
}
void displayString(char arr[ ][20],int n)
{
int i;
for(i = 0 ; i < n ; i++)
printf(" %s ",arr[i]);

© Cranes Varsity V2.1-2017-18 16/ 106


Advanced C & Data Structure www.cranesvarsity.com

}
main( )
{
int arr[N] = {5, 32, 8, 16, 9};
char name[N][20] = {"Swetha","Abhishek","Satish","Kunal"};
qsort(arr,N,sizeof(int),compareInt);
printf("\n THE SORTED ELEMENTS ARE \n");
displayInt(arr,N);
qsort(name,N,20,compareString);
printf("\n THE SORTED ELEMENTS ARE \n");
displayString(name,N);
}

Library functions for searching

void *bsearch(const void *key, const void *base, The bsearch function performs a binary
size_t num, size_t width, search of a sorted array of num elements,
int (*compar)(const void *, const void *)); each of width bytes in size.
void *lsearch(const void *key, void *base, size_t *num, The lsearch function performs a linear
size_t size, int(*compar)(const void *, const void *)); search for the value key in an array of num
elements, each of width bytes in size. If
key is not found, lsearch adds it to the
end of the array and increments num.
void *lfind(const void *key, const void *base, Similar to lsearch. If key is not found,
size_t *nmemb, size_t size, return NULL
int(*compar)(const void *, const void *));
There are several possible uses of pointer to function:
 In writing Device Driver programs for operating system

 In writing interrupt functions

 In writing viruses, vaccines to remove viruses

 In VC++ programming to connect events to function calls

 In developing COM/DCOM components

#pragma

The #pragma directive gives special instructions to the compiler. The #pragma directive is especially
useful in embedded C programming and can tell the compiler to allocate a certain variable in RAM or
EEPROM. It can also tell the compiler to insert a snippet of assembly language code.
The GNU GCC compiler, also uses attributes as an alternative syntax to the #pragma directive

Pragmas vary from one compiler to another.

#pragma pack modifies the current alignment rule for members of structures that follow this pragma.

Examples

This example shows how a #pragma pack directive can affect the size and mapping of a structure:
© Cranes Varsity V2.1-2017-18 17/ 106
Advanced C & Data Structure www.cranesvarsity.com

struct test
{
char a;
int b;
short c;
int d;
}S;

Void Pointers

A void pointer (void *ptr) is called as generic pointer. We can assign the address of any type of variable to
void pointer without casting and the address of void pointer can be assigned to any type of pointer without
casting.
int x = 10 , *iptr;
float y = 3.5 , *fptr;
void *vptr;

iptr = &x; //Valid


fptr = &x; // Not valid.
vptr = &x; //Valid

iptr = &y; //Not Valid


fptr = &y; // valid.
vptr = &y; //Valid

iptr = fptr; // Not Valid


iptr = vptr; //Valid

Restrictions on void pointer


void pointers should be not be used directly without typecasting to get expected results.

int main (void)


{
int num= 10;
char char_var = ‘u’;
void *ptr;
ptr = &num; /* Valid */

© Cranes Varsity V2.1-2017-18 18/ 106


Advanced C & Data Structure www.cranesvarsity.com

printf(“%d “, *(int *) ptr); /* Casting is a must */


ptr = &char_var; /* Valid */
printf(“%c “, *(char *) ptr); /* Casting is a must */
return 0;
}

Dynamic Memory Allocation

All the variables in every program up to this point have been either automatic or static variables. i.e., the
memory will be automatically allocated on declaring it and is de-allocated automatically when the control
goes out of the block in which it is declared.

Dynamic memory is the memory that does not exist when the program is loaded, but is created / allocated
dynamically as they are needed during the execution of the program. It is possible, using these
techniques, to create as much memory (till heap is not full) as needed, use them, and de-allocate their
memory space for reuse by other variables. Memory allocated dynamically will be allocated from the heap
segment.

Heap Memory

"Heap" memory, also known as "dynamic" memory is a large area of memory available for use by the
program. Heap memory is different in every way. The programmer explicitly requests the allocation of a
memory "block" of a particular size. In order to allocate a block of some size, the program makes an
explicit request by calling the heap allocation function. The allocation function reserves a block of memory
of the requested size in the heap and returns a pointer to it (Address). The block continues to be allocated
until the programmer explicitly requests that it has to be deallocated.
Nothing happens automatically. So the programmer has much greater control of memory, but with greater
responsibility.

The advantages of heap memory are...

 Lifetime. Because the programmer now controls exactly when memory is


allocated and deallocated, one can specify the lifetime of a data. This is not possible with
local memory which was automatically deallocated when the function terminated.
 Size. The size of the allocated memory can be controlled with more detail.
One can resize the memory at any time.

C provides access to the heap memory through a set of library functions. The prototypes for these
functions are in the file <stdlib.h>, so any code which wants to call these must include that header file.

malloc ( ): This function will try to get a contiguous block of memory of the required size. It returns a
pointer to the memory it found, if it could get some. If not it returns a NULL pointer. The syntax for the
malloc ( ) function is
void *malloc (size_t size);
You must always check to make sure that malloc is successful or not before you use the memory. malloc
has one parameter, the size of the block of memory you want. You can use sizeof to specify how much
memory you need, for example:
© Cranes Varsity V2.1-2017-18 19/ 106
Advanced C & Data Structure www.cranesvarsity.com

sizeof (int) * 99

would be enough space for 99 integers. Because malloc returns a pointer of type void it must be
typecasted to make this compatible with the type you want; i.e. if you want to store 99 integers you will
have a pointer to integers which you want to make point at the block of memory which malloc is going to
get for you. The cast will make sure that the compiler does not produce an error when it sees the
assignment,

Example

#include <stdio.h>
#include <stdlib.h>
int main (void)
{
char * buffer ;
int size;
printf(“ ENTER THE SIZE : “);
scanf(“ %d”,&size);
buffer = (char *) malloc (sizeof (char) * size) ;
if (buffer == NULL) {
printf("No memory!\n") ;
exit (-1);
}
else {
/* Do something with the memory */

© Cranes Varsity V2.1-2017-18 20/ 106


Advanced C & Data Structure www.cranesvarsity.com

}
return 0;
}

We can access an item in buffer in exactly the same way as we access elements in an array if memory
allocation is successful.

for ex. strcpy(buffer, “CRANES”);.

Note: The default value of the allocated memory is garbage.

free ( ):
The syntax for the free ( ) function is

void free(void* block)

free takes a pointer to a heap block earlier allocated and returns that block to the heap for re-use. After
the free ( ), the client should not access any part of the block or assume that the block is valid memory.
The block should not be freed a second time.

It is good programming practice to always give back memory which you have allocated. If you do not you
might find that the total amount of memory available will drop to a point where your program will not run
any more.

Problems associated with free


 Dangling pointers
Dangling pointers are pointers that do not point to a valid object.
Dangling pointers arise when an object is deleted or deallocated, without modifying the value of the
pointer, so that the pointer still points to the memory location of the deallocated memory
 Free memory which is already deallocated (double free)
 Free memory of stack / data segment
 Pointer not pointing to beginning of block.

Pointer not pointing to beginning of block Deallocating Non-Heap Memory


int main()
{ int main()
int *ptr = malloc(20); {
ptr++;
free(ptr); int arr[5]
} int *ptr = arr;
free(ptr);
}

© Cranes Varsity V2.1-2017-18 21/ 106


Advanced C & Data Structure www.cranesvarsity.com

Double Free
int main()
{
int *ptr = malloc(20);
printf(“ ptr = %p \n“,ptr);
free(ptr);
printf(“\n ptr = %p \n“,ptr);
free(ptr);

calloc ( )
Besides the malloc ( ) function, you can also use the calloc ( ) function to allocate a memory dynamically.
The differences between the two functions are that the latter takes two arguments and that the memory
space allocated by calloc ( ) is always initialized to 0. The syntax for the calloc( ) function is

void *calloc (size_t nitem, size_t size);

Here nitem is the number of items you want to save in the allocated memory space. size gives the
number of bytes that each item takes. The calloc ( ) function returns a void pointer too. If the calloc ( )
function fails to allocate a piece of memory space, it returns a null pointer.

/* Demo program on calloc ( ) function. */


#include <stdio.h>
#include <stdlib.h>
int main(void)
{
float *ptr1;
int i, n = 5;
ptr1 = (float *) calloc (n, sizeof (float));
if (ptr1 == NULL)
printf("calloc( ) failed.\n");
else
{
for(i = 0; i < n; i++)
printf("ptr1[%d]=%5.2f\n", i, * (ptr1 + i));
free(ptr1);
}
return 0;
}

Realloc ( )

The realloc( ) function gives you a means to change the size of a piece of memory space allocated by the
malloc( ) or the calloc( ) function, or even itself.
The syntax for the realloc ( ) function is

void *realloc(void *block, size_t size);


© Cranes Varsity V2.1-2017-18 22/ 106
Advanced C & Data Structure www.cranesvarsity.com

Here block is the pointer to the start of a piece of memory space previously allocated. size specifies the
total byte number you want to change to. The realloc( ) function also returns a void pointer. If the realloc (
) is successful then it returns the address. i.e., if the user wants to increase the memory allocated to the
pointer, then there is a possibility of giving a new memory address by copying the data from old memory
into new memory or by extending the existing memory realloc ( ) returns the same address what was
there earlier. That’s why it is necessary that we have to collect the return value of realloc( ) function in a
new pointer. The realloc( ) function returns a null pointer if it fails to reallocate a piece of memory space.
The realloc( ) function is equivalent to the malloc( ) function if the first argument passed to realloc( ) is
NULL. In other words, the following two statements are equivalent:

ptr_flt = realloc (NULL, 10 * sizeof (float));

ptr_flt = malloc (10 * sizeof (float));

Also, you can use the realloc ( ) function as the free ( ) function. You do this by passing 0 to realloc ( ) as
its second argument. For instance, to release a block of memory pointed to by a pointer ptr, you can
either call the free ( ) function like this:

free (ptr);
or use the realloc( ) function in the following way:
realloc (ptr, 0);

/******************** program.c ***************/

#include <stdio.h>
#include <stdlib.h>
int main( )
{
float *ptr1,*ptr2;
int i, n = 5;

ptr1 = (float *) malloc (n * sizeof(float));


if (ptr1 == NULL)
printf("calloc( ) failed.\n");
else
{
N = 10;
ptr2 = (float *) realloc(ptr1, n * sizeof(float));
if (ptr2 == NULL)
fprintf(stderr, “Error : In Realloc( ) \n “);
else if (ptr1 != ptr2) /* If new block of memory is allocated */
{
ptr1 = ptr2; /*Make ptr1 point to new block*/
}
else
{
/* Use ptr1. */
}
}
return 0;
© Cranes Varsity V2.1-2017-18 23/ 106
Advanced C & Data Structure www.cranesvarsity.com

Memory Leaks

What happens if some memory in heap is allocated, but never deallocated? A block or function which
forgets to deallocate a block is said to have a "memory leak" which may be a serious problem. The result
will be that the heap gradually fills up as there continue to be allocation requests, but no deallocation
requests to return blocks for re-use.

For a program which runs, computes something, and exits immediately, memory leaks are not usually a
concern. Such a "one shot" program could omit all of its de-allocation requests and still mostly work.
Memory leaks are more of a problem for a program which runs for an indeterminate amount of time. In
that case, the memory can gradually fill the heap and further memory allocation requests cannot be
satisfied, and the program stops working or crashes. Many commercial programs have memory leaks, so
that when run for long enough, or with large data-sets, they fill their heaps and crash. Often the error
detection and avoidance code for the heap-full error condition is not well tested, precisely because the
case is rarely encountered with short runs of the program — that's why filling the heap often results in a
real crash instead of a polite error message. Most compilers have a "heap debugging" utility which adds
debugging code to a program to track every allocation and deallocation. When an allocation has no
matching deallocation, that's a leak, and the heap debugger can help you find them.

/************************************ Program *************************************************/


int *pi;
void foo() {
pi = (int*) malloc(8*sizeof(int));
/* Allocate memory for pi */
/* Ohh!! leaked the old memory pointed to by pi */

free(pi); /* foo() is done with pi, so free it */
}
int main() {
pi = (int*) malloc(4*sizeof(int));
foo();
free(pi) /* pi is dangling pointer */
}

Command line arguments

Like most of the function the main can also take the arguments. To pass the values to the main function
as an argument we need to specify the values along with the executable file name on the command line.
This is known as command line argument.
Example the vi command itself will take the file name as an argument to open the file
$ vi Fcopy.c

Here vi is a command which is taking the argument Fcopy.c from the command line. In order to be able to
use such arguments in our code we must define them as follows: int
main(int argc, char *argv[ ])
 argc is the number of command line arguments (including the program name).

© Cranes Varsity V2.1-2017-18 24/ 106


Advanced C & Data Structure www.cranesvarsity.com

 argv is a pointer to an array of character type, holding each command line argument -- including
the program name in the first array element.
/* program to print arguments from command line and to find the sum total of the given
numbers */
#include<stdio.h>
int main (int argc, char *argv[ ])
{
int i, sum = 0 ;
printf(“\ntotal no of arguments = %d\n”,argc);
if (argc == 1)
{
fprintf(stderr, ”\nenter properly”);
exit(1); /* exit on failure */
}
for (i = 0; i < argc; ++i)
printf(“argv[%d]: %s\n”, i, argv[i]);
for (i = 1; i < argc; ++i)
sum = sum + atoi (argv[i]); /* ascii to integer conversion */
printf(”sum = %d”, sum);
return 0;
}

String conversion functions

int atoi(const char *nptr); convert a string to a integer.


long atol(const char *nptr); convert a string to a long.
double atof(const char *nptr); convert a string to a double.
char *gcvt( double value, int digits, char *buffer Converts a floating-point value to a string, which it stores
); in a buffer.

Functions to copy a block of memory [any type]


Buffer manipulation functions in C work on the address of the memory block rather than the values inside
the address.

Examples: memset(), memcpy(), memmove(), memcmp()

void *memset(void *s, int c, size_t n); The memset() function fills the first n bytes of
the memory area pointed to by s with the constant
byte c
void *memmove(void *dest, const void *src, size_t The memmove() function copies n bytes from
n); memory area src to memory area dest. The
memory areas may overlap.
void *memcpy(void *dest, const void *src, size_t n); The memcpy() function copies n bytes from
memory area src to memory area dest. The
memory areas may not overlap.
© Cranes Varsity V2.1-2017-18 25/ 106
Advanced C & Data Structure www.cranesvarsity.com

int memcmp(const void *s1, const void *s2, size_t The memcmp() function compares the first n bytes
n); of the memory areas s1 and s2.

Example: memset()
#include <stdio.h>
#include <string.h>
int main ()
{
char str[50];
strcpy(str,"This is string.h library function");
puts(str);
memset(str,'$',7);
puts(str);
return(0);
}

On running the above code, the first seven characters will be changed to $.

Example: memcpy()

#include <stdio.h>
#include <string.h>
int main ()
{
const char src[50] = "http://www.cranessoftware.com";
char dest[50];
printf("Before memcpy dest = %s\n", dest);
memcpy(dest, src, strlen(src)+1);
printf("After memcpy dest = %s\n", dest);
return(0);
}

Initially dest would be empty. After memcpy() , the content is copied to that location.

© Cranes Varsity V2.1-2017-18 26/ 106


Advanced C & Data Structure www.cranesvarsity.com

Chapter 3
RECURSION

Recursion is a programming technique that allows the programmer to express operations in terms of
themselves. In C, this takes the form of a function that calls itself. A useful way to think of recursive
functions is to imagine them as a process being performed where one of the instructions is to "repeat the
process".
A function that calls itself is known as a recursive function. And, this technique is known as recursion.

void recursion()
{
recursion(); /* function calls itself */
}
int main()
{
recursion();
}

The important point to remember while using recursion is that the programmers need to be careful to
define an exit condition from the function, otherwise it will go into an infinite loop.
Recursive functions are very useful to solve many mathematical problems, such as calculating the
factorial of a number, generating Fibonacci series, etc.

© Cranes Varsity V2.1-2017-18 27/ 106


Advanced C & Data Structure www.cranesvarsity.com

Usage of Stack in Recursion

Stack is used during recursion. When a function is called, the return address, the values of local variable
and formal parameters will be pushed on to the stack. Then control enters into the function. Once the
return statement is executed, control comes back to the same point from where it had been called and the
returned value is substituted at that point and the control goes to the statement, which immediately
follows the function call. The portion of the stack which corresponds to a particular function is called as
stack frame. The frame pointer (fp) points to the start of the stack frame and does not move for the
duration of the subroutine call.This points to the base of the stack frame, and the parameters that are
passed in to the subroutine remain at a constant offset relative to the frame pointer. However, as
variables are placed on the stack, the stack pointer (sp) moves.

Example: Sum of Natural Numbers Using Recursion

#include<stdio.h>

int sum(int n);

int main()

int number,result;

printf(“enter the number!”);

scanf(“%d”,&number);

result = sum(number);

printf(“sum: %d”,result);

int sum(int num)

if(num!=0)

return num + sum(num-1); // recursion : sum() calls itself

else

return num;

© Cranes Varsity V2.1-2017-18 28/ 106


Advanced C & Data Structure www.cranesvarsity.com

Example: Number Factorial


The following example calculates the factorial of a given number using a recursive function
#include <stdio.h>
int factorial(unsigned int i)
{
if(i <= 1)
{
return 1;
}
return i * factorial(i - 1);
}

int main()
{
int i = 15;
printf("Factorial of %d is %d\n", i, factorial(i));
return 0;
}

© Cranes Varsity V2.1-2017-18 29/ 106


Advanced C & Data Structure www.cranesvarsity.com

Fibonacci Series

The following example generates the Fibonacci series for a given number using a recursive function.

#include <stdio.h>

int fibonaci(int i) {
if(i == 0) {
return 0;
}
if(i == 1) {
return 1;
}
return fibonaci(i-1) + fibonaci(i-2);
}

int main() {
int i;
for (i = 0; i < 10; i++) {
printf("%d\t\n", fibonaci(i));
}
return 0;
}

NOTE : Recursion makes program elegant and cleaner. All algorithms can be defined recursively which
makes it easier to visualize and prove. If the speed of the program is vital then, you should avoid using
recursion. Recursions use more memory and are generally slow. Instead, you can use looping constructs.

© Cranes Varsity V2.1-2017-18 30/ 106


Advanced C & Data Structure www.cranesvarsity.com

CHAPTER 4
STRUCTURES
We have seen earlier how normal variables can hold one piece of information and how arrays can be
used to represent group data items of the same type. Suppose we want to deal with entities which is a
collection of different data types using a single name then we cannot use arrays. Fortunately, C supports
a constructed data type known as structure. Structures provide for grouping variables of different data
type under a single name.

Syntax
struct tag_name
{
type member1;
type member2; Elements or
--------- members of structure.
};

The first line contains the struct keyword, then the tag_name. member1, member2… are the members of
the structure. The members can be ordinary variables, pointers, arrays or of type structure again.
Members cannot be initialized within a structure declaration.

Once a structure is defined, a variable of its type can be created by using the tag name struct tag_name
variable1, variable2,…; Each of these variables contains all the members specified in the structure
definition.

For example, suppose we want to store data about a student, which consists of student name (a string),
regno (an integer), fees (a float), section (a character) and so on. Then we create a structure variable as
below.

struct student
{
char name[10];
int rollno;
float fees;
char section;
};

Note: The above declaration has not declared any variable it simply describes a format called template to
represent the information as shown below.

struct student s1;


© Cranes Varsity V2.1-2017-18 31/ 106
Advanced C & Data Structure www.cranesvarsity.com

This statement sets aside space in memory. It makes available space to hold all the elements in the
structure. Here s1 is a structure variable of type student. We can combine the declaration of a structure
and structure variable in one statement.
Example
struct student
{
char name[10];
int rollno;
float fees;
char section;
}s1, s2;

We can also write a structure declaration without the tagname as seen below. We can define as many
variables as we want immediately after the closing brace. But we will not be able to define variables of
this type later in our program. Since the tagname identifying this structure type is not available.

struct
{
char name[10];
int rollno;
float fees;
char section;
}s1, s2;

Note
 Members of the structure themselves are not variables. They do not occupy any memory until
they are associated with a structure variable.
 The structure declaration should be terminated with a semicolon.

Initializing structure variable

Like ordinary variables and arrays, structure variables can also be initialized
For example:
struct student
{
char name[10];
int rollno;
float fees;
char section;
}s1={“princy”,123,4000,’A’};
struct student s2 = {“suman”,124,4000,’B’};
struct student s3 = {“bob”,125,4000,’C’};

Accessing structure elements

Structure members or elements are accessed using the dot (.) operator
For example to access the elements of the above structure the syntax is

Variable.element;
© Cranes Varsity V2.1-2017-18 32/ 106
Advanced C & Data Structure www.cranesvarsity.com

Example:
s1.rollno;
s1.fees;

Structures and pointers

We can have a pointer pointing to structures; such pointers are known as structure pointers.

#include <stdio.h>

int main(void)
{
struct student
{
char name[12];
int rollno;
float fees;
};
struct student s = {"suman",12,2000};
struct student *ptr;
ptr = &s;
printf("Name: %s\t",ptr->name);
printf("Rollno: %d\t",ptr->rollno);
printf("Fees: %f\n",ptr->fees);
return 0;
}

The address of the structure s is assigned to the pointer ptr by writing

ptr = &s;

An individual structure element can be accessed using ptr using the -> operator

ptr->name;

We can use (*ptr).name also in printf statement like

© Cranes Varsity V2.1-2017-18 33/ 106


Advanced C & Data Structure www.cranesvarsity.com

printf(“name = %s\t”, (*ptr).name);

The parentheses are required because the dot operator (.) has higher precedence then the indirection
operator (*).

Without the parenthesis the compiler would generate error because ptr(a pointer) is not directly
compatible with the dot operator.

Comparison of structure variables

We cannot use relation operators to compare between structures. Individual members have to be
compared using relational and logical operators.
Example
/*Comparison of structure variables*/
#include <stdio.h>
#include <string.h>
struct student
{
char name[12];
int rollno;
float fees;
};
int main( )
{
struct student st1={"suman",12,2000};
struct student st2;
st2 = st1;
/* if(st1 == st2) gives error */
if((strcmp(st1.name,st2.name)==0)&&(st1.fees==st2.fees)&&
(st1.rollno==st2.rollno))
printf("They are equal");
else
printf("\nThey are not equal");
printf("\n%s\t%d\t%f\n",st1.name,st1.rollno,st1.fees);
printf("\n%s\t%d\t%f\n",st2.name,st2.rollno,st2.fees);
return 0;
}

© Cranes Varsity V2.1-2017-18 34/ 106


Advanced C & Data Structure www.cranesvarsity.com

Passing structures to Functions:

 Passing individual structure elements to function


 Passing entire structure to function.

Either method pass by value or pass by address can be used.


Example:
/* Passing individual structure elements to function */
#include <stdio.h>
void display(char *n, int r,float f)
{
printf("\nName:%s\nRollno:%d\nFees:%f\n",n,r,f);
}
int main( )
{
struct student
{
char name[12];
int rollno;
float fees;
};
struct student st={"suman",12,2000};
display(st.name,st.rollno,st.fees);
return 0;
}

It is not possible to pass the st variable as an argument to display( ) function, as the display( ) don’t know
the syntax of student. Therefore the function display is called using

display (st.name,st.rollno,st.fees);

i.e. we are passing the base address of the array name, but the other two arguments rollno and fees by
value. Thus this is a mixed call: a call by reference as well as a call by value.
Passing individual structure elements becomes tedious for structures with more elements. A better way is
pass the entire structure variable to the function at a time.

Example
/* Passing entire structure to function */

#include <stdio.h>
struct student
{
char name[12];
int rollno;
float fees;
};
void display(struct student s)
{
printf("Name : %s\t",s.name);
© Cranes Varsity V2.1-2017-18 35/ 106
Advanced C & Data Structure www.cranesvarsity.com

printf("Rollno: %d\t",s.rollno);
printf("Fees : %f\n",s.fees);
}
int main( )
{
struct student st = { "suman",12,2000};
display (st);
return 0;
}

Here calling the function display( ) is compact

display(st);

The formal argument in the function should be declared as

struct student s;
The structure is declared globally, so the function display ( ) can take an argument as student in the
program

Passing address of a structure variable as an argument to function

Example
/*Passing the address of the structure as argument */

#include <stdio.h>
struct student
{
char name[12];
int rollno;
float fees;
};
void display(struct student *s)
{
printf("Name : %s\t", s->name);

printf("Rollno: %d\t", s->rollno);

printf("Fees : %f\n", s->fees);


}
int main( )
{
struct student st = { "suman", 12, 2000 };
display (&st);
return 0;
}

© Cranes Varsity V2.1-2017-18 36/ 106


Advanced C & Data Structure www.cranesvarsity.com

Array of structures

In an array of structures all elements of the structure are stored in adjacent memory locations. Since each
element in this array is a structure and since all structure elements are always stored in adjacent
locations we can very well visualize the arrangement of array of structures in memory as below
Eg: struct student s[10]; //will be stored as shown below.

/* usage of array of structures */


#include <stdio.h>
#define NAME_SIZE 20
#define NO_STUDENT 10
int main( )
{
struct student
{
char name[NAME_SIZE];
int rollno;
float fees;
} s[NO_STUDENT];
int i;
/* input student details */
for(i = 0; i < NO_STUDENT; i++)
{

© Cranes Varsity V2.1-2017-18 37/ 106


Advanced C & Data Structure www.cranesvarsity.com

printf("\nEnter name:");
gets(s[i].name);
printf("\nEnter rollno:");
scanf ("%d",&s[i].rollno);
printf("\nEnter fees:");
scanf ("%f",&s[i].fees);
}
/* print the records*/
for(i = 0; i < NO_STUDENT; i++)
{
printf("\n\nName:%s",s[i].name);
printf("\nRollno:%d",s[i].rollno);
printf("\nFees:%f",s[i].fees);
}
return 0;
}

Here we have an array called s which is of the type struct student. This provides space in memory for 10
structures of the type struct student. The syntax for referring to each element of the array is similar to
the syntax used for ordinary arrays.

Structure within structure

Nesting of the structures is permitted in C.

Eg : structure to store date


struct date
{
int dd;
int mm;
int yy;
};
struct student
{
char name[10];
int rollno;
float fees;
struct date doj;
}s;

The structure date has been made as the member of structure student.

© Cranes Varsity V2.1-2017-18 38/ 106


Advanced C & Data Structure www.cranesvarsity.com

To access the elements of the structure date, this is the part of another structure.
s.doj.dd;
s.doj.mm;

name

rollno

fees

doj

dd

mm

yy

Self-referential structures

It is a structure which contains one member which is a pointer of its own type.

Example
struct student
{
char name [12];
int rollno;
float fees;
struct student *next;
};

The above structure contains an element/member which is a pointer to a structure of the same type (i.e.,
a pointer to structure of type student), called next. Therefore this is a self-referential structure. Self
referential structures are very useful in applications that involve linked data structures, such as lists and
trees

Bit Fields: Memory saving structures

In a scalar data object, the smallest object that can be addressed directly is usually a byte. In a structure,
you can define data objects which can be as small as 1 bit using bit-fields. A bit-field is an integer variable
© Cranes Varsity V2.1-2017-18 39/ 106
Advanced C & Data Structure www.cranesvarsity.com

that consists of a specified number of bits. If you declare several small bit-fields in succession, the
compiler packs them into a single machine word. This permits very compact storage of small units of
information. Of course, you can also manipulate individual bits using the bitwise operators, but bit-fields
offer the advantage of handling bits by name, like any other structure.

The declaration of a bit-field has the form:

type member_name : width ;

 The type should be of integer type (int / short / long / character).


 Float and double type is not allowed
 Width - The number of bits in the bit-field. The width must be a constant integer expression
whose value is non-negative, and must be less than or equal to the bit width of the specified type.
 We cannot get the address of bit field members
 Bit-field cannot be applied for arrays / pointers.

Example
struct Date
{
unsigned int month : 4; // 1 is January; 12 is December.
unsigned int day : 5; // The day of the month (1 to 31).
unsigned int year : 12;
};

We can initialize an object of type struct Date in the normal way, using an initialization list:

struct Date birthday = { 5, 17, 1982 };

The object birthday occupies the same amount of storage space as a 32-bit int object.

Example :
struct Student
{
int regno;
char name[20];
unsigned int brCode : 3; // Can store values from 0 to 7.
unsigned int sem : 3; // Can store values from 0 to 7.
unsigned int gender : 1;
};

Accessing the bit-field members is same as accessing any other members of struct / union.
Eg., struct Student s;
s.regno = 5;
s.sem = 2;

Union

Unions are derived data-types. Unions like structures, contain members of dissimilar data types. However
all members of the union share the same memory, whereas each member of the structure is assigned

© Cranes Varsity V2.1-2017-18 40/ 106


Advanced C & Data Structure www.cranesvarsity.com

separate memory; that is, all members of a union start at the same address. Thus you can define a union
with many members, but only one member can contain a value at any given time.

The general form of union is


union tag_name
{
members 1;
members 2;
...
members n;
};
the union variables are then declared
union tag_name variable1,variable2;

The compiler will allocate enough storage to accommodate the largest element in the union.

The union elements are accessed exactly the same way as that of structure elements using dot(.)
operator and if the union variable is a pointer then arrow(->) operator is used.
Initializing the union variables
Only first member of a union can be initialized at a time.

Structures and Unions

We can have nested unions or there can be a nesting on structure and a union or a union and a structure.

Example
typedef struct
{
unsigned mantissa : 23;
unsigned exponent : 8;
unsigned sign : 1;
}FLOAT_t;

union
{
float f;
FLOAT_t ff;
char c[4];
}u;
int main(void)
{
float a;

© Cranes Varsity V2.1-2017-18 41/ 106


Advanced C & Data Structure www.cranesvarsity.com

int i;
printf("\n ENTER A FLOAT NUMBER :");
scanf("%f",&u.f);
printf("\n sign = %d exponent = %d \
mantissa = %x",u.ff.sign,u.ff.exponent,u.ff.mantissa);
for(i = 0 ; i < 4; i++)
printf(" %x ",u.c[i]);
return 0;
}

Enumerated data types

Enumerated data types will help us to create one more type of user defined data type where the
identifiers in the enum data type will represent the integer values only. Its general form is

enum tag_name{ member1, member2,…membern };


Here, enum is the keyword; tag_name is the name of the enumerated data-type Once the enumerated
data-type is defined the variables of that type can be created as follows

enum tag_name variable1,variable2,..variablen;

The definition and the variable declaration can be combined as follows

enum tag_name { member1, member2,…memberN } variable1,variable2;


here the tag_name is optional and variable1 and variable2 are the variables of type enum.

Internally the compiler treats the enumeration constants as integers and assign's values beginning from 0
for first member, with each successive member increasing by 1 and so on.

Example:
enum weekday {MON, TUE, WED, THU, FRI}; /* defining enumerated data-type*/
enum weekday d2; /* creating a variable of that type*/

The enumeration constants will represent the following values.


MON  0
TUE  1
WED  2
THU  3
FRI  4

Uses of enumerated data types


Enumerated variables are usually used to clarify the operation of a program. For example,

Example
int main( )
© Cranes Varsity V2.1-2017-18 42/ 106
Advanced C & Data Structure www.cranesvarsity.com

{
enum DAYS_OF_WEEK {SUN,MON, TUE,WED,THU,FRI,SAT} d1, d2;
d1 = TUE;
d2 = WED;
printf(“No of days between d1 & d2 :", d2 - d1);
if(d1 > SUN && d1 < SAT) /*They can be compared */
printf(“Valid Week Day\n");
else
printf("Invlalid Week Day\n");
switch(d1)
{
case MON: printf(“\nMonday");
break;
case TUE: printf("\nTuesday");;
break;
case WED: printf("\nWednesday");
break;
case THU: printf("\nThursday");
break;
case FRI: printf("\nFriday");
break;
default: printf("\nInvalid day");
}
return 0;
}

These automatic assignments can be overridden within the definition of the enumeration by assigning
some of the constants with explicit integer values which differ from the default values. Thus the constants
which are not assigned explicit values will automatically be assigned values which increase by 1 from the
last explicit assignment. This may cause two or more enumeration constants to have the same integer
values.
enum weekday {MON=1, TUE, WED, THU, FRI};

The member MON is assigned a value 1, TUE will be represent the value 2, WED represents 3 and so
on. i.e., the other members are automatically assigned values that increase successively by 1.

Note
It is not possible to collect the value to an enum variable using the input statements.

typedef
The Keyword typedef is used to give an alternate name for an existing datatype.
typedef to used to create shorter or simpler type names.
Let's see how typedef works. Suppose you want to use the term BYTE for one-byte numbers. You simply
define BYTE as if it were a char variable and precede the definition by the keyword typedef, like
typedef unsigned char BYTE;

From then on, you can use BYTE to define variables.

BYTE x, y[10], * z;

© Cranes Varsity V2.1-2017-18 43/ 106


Advanced C & Data Structure www.cranesvarsity.com

typedef unsigned int size_t;


Defines a new type name size_t as a synonym for type unsigned int

size_t no_elements;
size_t class_strength;
void bubblesort(int arr[] , size_t n);

Similarly, the declaration


typedef char *String_t;
Defines a new type name String _t as a synonym for type char *
String_t s1 = “CRANES”;
size_t Strlen(String_t str);

typedef can also be applied to structures and union


Eg: struct Student
{
int id;
char name[20];
int sem;
};
typedef struct Student STUDENT;
Now STUDENT can be used as the datatype.
STUDENT s1 , s2[5];

Typedef can be combined with structure declaration


typedef struct Student typedef struct
{ {
int id; int id;
char name[20]; char name[20];
int sem; int sem;
}STUDENT; }STUDENT;

Using typedef does not define any objects or storage.


typedef does not create a new data type; it only adds a new name for existing type.
Typedef names allow you to encapsulate implementation details that may change
Use typedef to help make a program more portable.

Typedef can also be applied to function pointers


Eg : typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

Type Qualifiers

Type qualifiers are used to tell the compiler that variables(objects) of these types will need special
consideration. There are two type qualifiers in C
 const  volatile

© Cranes Varsity V2.1-2017-18 44/ 106


Advanced C & Data Structure www.cranesvarsity.com

The const keyword in a declaration establishes an entity whose value cannot be modified by assignment
or by incrementing or decrementing.

The code
const int nochange = 10; /* qualifier nochange as being constant */
nochange = 12; /* not allowed */

The preceding declaration makes nochange a read–only variable. After it is initialized, it cannot be
changed. If you inadvertently try to modify an entity defined as a const, the compiler will generate an
error/warning. This is an indication to you that something is wrong. Declaring an entity as const allows the
optimizer to do a better job which could make your program run a little faster.

Since constants can never have a value assigned to them in the executable part of the program, they
must always be initialized. The purpose of volatile is to force an implementation to suppress optimization
that could otherwise occur.

Using const with Pointers and Parameters Declarations


Using the const keyword while declaring a simple variable and an array, is pretty easy. Pointers are more
complicated because you have to distinguish between making the pointer itself const(constant pointer)
and making the value is pointed to const(pointer to constant).

The declaration
const float *pf; /* pf is a pointer to float constant */

establishes that pf points to value, that must remain constant. The value of pf itself can be changed. For
example, it can be set to point at another variable. In contrast, the declaration

float *const pt; /* pt is a const pointer to float */


says that the pointer pt itself cannot have its value changed. It must always point to the same address,
but the pointed to value can change. Finally, the declaration
const float * const ptr; /* ptr is a constant pointer to float constant */
means ptr must always point to the same location and that the value stored at the location must not
change. There is third location in which you can place const:

float const *pfc; /* same as const float * pfc; */

As the comment indicates, placing const after the type name and before the * means the pointer can’t be
used to change the pointer-to value. In short, a const anywhere to the left of the *, makes the data
constant and a const to the right of the *, makes the pointer itself constant. One common use for const is
declaring pointers that serve as formal function parameters. For example, suppose you have a function
called display( ) that displays the contents of an array.

To use it, you would pass the name of the array as an actual argument, but the name of an array is an
address, that would enable the function to alter data in the calling function. But the following prototype
prevents this from happening

void display(const int array[ ],int limit);

In a prototype and a function header, the parameter declaration const int array[ ] is the same as const int
* array, so the declaration says that the data to which array points cannot be changed.

© Cranes Varsity V2.1-2017-18 45/ 106


Advanced C & Data Structure www.cranesvarsity.com

The ANSI C library follows this practice. If a pointer is used only to give a function access to values, the
pointer is declared as a pointer to a const-qualifier type. If the pointer is used to alter data in the calling
function, the const keyword isn’t used. For example, the ANSI C declaration for strcat( ) is this:

char *strcat(char *,const char *);


Recall that strcat( ) adds a copy of the second string to the end of the first string. This modifies the first
string, but leaves the second string unchanged. The declaration reflects this.
Volatile
Volatile is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of
the variable may change at any time—without any action being taken by the code the compiler finds
nearby. The implications of this are quite serious. However, before we examine them, let’s take a look at
the syntax.
Syntax
To declare a variable volatile, include the keyword volatile before or after the data type in the variable
definition. For instance both of these declarations will declare foo to be a volatile integer:
volatile int foo;
int volatile foo;

Now, it turns out that pointers to volatile variables are very common. Both of these declarations declare
foo to be a pointer to a volatile integer:
volatile int * foo;
int volatile * foo;

When to Use Volatile


A variable should be declared volatile whenever its value could change unexpectedly. If a variable is
declared as volatile, the we are instructing the compiler to turn off the optimization process for that
variable i.e we are forcing the compiler to read the value of that variable from memory only, each time it is
encountered in the program.
If we use the qualifier const along with the qualifier volatile, then the value of volatile variable cant be
changed from inside the program.For example
const volatile int x;
if an array, structure or union is declared as volatile then each member becomes volatile.

© Cranes Varsity V2.1-2017-18 46/ 106


Advanced C & Data Structure www.cranesvarsity.com

CHAPTER 5
FILE I/O

So far, we used getchar ( ) and scanf ( ) functions to get the data from the terminal and putchar ( ) and
printf( ) functions to display the data on the terminal. It is very likely that many of the programs that we will
develop will be able to perform all of their I/O operations using just these functions. However, situations
do arise where we need more flexibility to work with files. For ex., we may need to write or read data from
one or more number of files and to handle these situations, we need special functions, called disk i/o
functions, designed for the purpose of working with files.

Disk I/O Functions

Disk I/O operations are performed on entities called files using the standard I/O functions. These
functions can be divided into two

 High level file I/O functions (standard I/O or stream I/O functions)
 Low level file I/O functions. (System I/O functions)

The high level functions are commonly used in C programs since they are easier to use than low level I/O
functions; because the high level I/O functions do their own buffer management, whereas in low level file
I/O function buffer management has to be explicitly done by the programmer. The high level file I/O
functions are further categorized into text and binary. This classification arises out of the mode in which a
file is opened for input and output.

C can process binary files. Not all systems support binary files. Files opened as text files if binary mode
not supported. Binary files should be used when rigorous speed, storage, and compatibility conditions
demand it. Otherwise, text files preferred. Which of these two modes is used to open the file determines

 How new lines (\n) are stored.


 How end of file is indicated.
 How numbers are stored in the file.

Text Mode

 The EOF is marked with a special character whose ASCII value is 26.
 Numbers are stored as strings of characters
 A newline character is converted into the carriage return-linefeed combination before being
written to disk and converted back to newline when file is read by the C program.

Binary Mode

 The EOF conversion does not take place. There is no such special character. They keep track of
the EOF from the number of characters present in the directory entry of the file.
 Numbers occupy the same number of bytes on disk as it occupies in memory

Opening a file

Before we write information to a file or read information from a file on the disk, we must open the file.
Opening the file establishes a link between the program and the operating system, about, which file are
© Cranes Varsity V2.1-2017-18 47/ 106
Advanced C & Data Structure www.cranesvarsity.com

we going to access and how. We provide the operating system with the name of the file and whether we
plan to read or write to it.

The link between our program and the operating system is a structure called FILE which has been
defined in the header file “stdio.h”. Therefore it is necessary to include this file when we are doing high
level disk I/O. When we request the OS to open a file, what we get back is a pointer to the structure FILE.
Also known as file pointer, it is a way of identifying a particular file within the program that wants to
perform some I/O operation on that file. That is why the following declaration is required.

FILE *fp;

Each file we open will have its own FILE structure. The file structure contains information about the file
being used, such as

 Its current size.


 Its location in memory
 A character pointer which points to the character that is about to be read.

The commonly used standard C-library functions for file handling can be categorized as follows

Opening Stream

NAME fopen - opens the specified file in the specified mode

SYNOPSIS

#include<stdio.h>

FILE *fopen (const char *path, const char *mode);

RETURN VALUES

Upon successful completion fopen returns a FILE pointer. Otherwise, NULL is returned.

DESCRIPTION

The fopen function opens the file whose name is the string pointed to by path and associates a stream
with it. The argument mode points to a string beginning with one of the following sequences (Additional
characters may follow these sequences.)

Table : File opening modes

Mode Description
R Open the file for reading only. The stream is positioned at the beginning
of the file.
r+ Open for reading and writing. The stream is positioned at the beginning
of the file.

© Cranes Varsity V2.1-2017-18 48/ 106


Advanced C & Data Structure www.cranesvarsity.com

W Truncate file to zero length or create text file for writing. The stream is
positioned at the beginning of the file.
w+ Open for reading and writing. The file is created if it does not exist,
otherwise it is truncated. The stream is positioned at the beginning of the
file.
A Open for writing. The file is created if it does not exist. The stream is
positioned at the end of the file.
a+ Open for reading and writing. The file is created if it does not exist. The
stream is positioned at the end of the file.

NAME fclose – close a stream

SYNOPSIS

#include<stdio.h>
int fclose (FILE *stream);

DESCRIPTION

The fclose function dissociates the named stream from its underlying file or set of functions. If the stream
was being used for output, any buffered data is written first, using fflush.

RETURN VALUES

Upon successful completion 0 is returned. Otherwise, EOF is returned and the global variable errno is set
to indicate the error. In either case no further access to the stream is possible.

Example

/* file opened for reading*/

#include <stdio.h>
int main (void)
{
FILE *fp;
if ((fp = fopen (“cranes”, “r”)) == NULL)
{
printf(“Error in opening the file\n”);
}
else
{
printf(“File is opened\n”);
fclose (fp); /* file is closed */
}
return 0;
}

© Cranes Varsity V2.1-2017-18 49/ 106


Advanced C & Data Structure www.cranesvarsity.com

EXIT function

At times, it may be desirable to force the termination of a program, such as when an error condition is
detected by a program. We know that program execution is automatically terminated whenever the last
statement in main is executed. But in order to explicitly terminate a function, the exit function call be
called. The function call

exit (n);

has the effect of terminating the current program. Any open files will be automatically close by the system.
The integer value n is called the exit status. Under UNIX, this value is 0 by convention for a program that
terminates normally, and non zero for a program that terminates due to some detected error condition.
This condition code may be tested by other processes to determine whether the program successfully
completed execution or not.

For example

The following statements will cause the program to terminate with a condition code value of 1 if the file
named cranes cannot be opened for reading.

#include <stdio.h>
int main( )
{
FILE * fp;
if ((fp = fopen(“cranes”, “r ”)) == NULL)
{
printf(“File cannot be opened for reading\n”);
exit(1);
}
printf(“File is opened for Reading\n”);
fclose(fp);
return 0;
}
Reading and Writing to Streams:

NAME fgetc – Read a character from the file stream

SYNOPSIS

#include <stdio.h>

int fgetc (FILE *stream);

RETURN VALUES

Returns the character read as an unsigned char cast to an int or EOF on end of file or Error.

DESCRIPTION

© Cranes Varsity V2.1-2017-18 50/ 106


Advanced C & Data Structure www.cranesvarsity.com

fgetc ( ) reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on
end of file or error.

NAME fputc – send a character to the file stream.

SYNOPSIS

#include<stdio.h>

int fputc (int c, FILE *stream);

RETURN VALUES

The character written as an unsigned char cast to an int or EOF on error.

DESCRIPTION

fputc( ) writes the character c, cast to an unsigned char, to stream.

Example

/* copying one file to another file */

#include <stdio.h>
int main(int argc,char **argv)
{
FILE *ifp, *ofp;
int c;
if (argc != 3)
{
fprintf(stderr,”Wrong no. of arguments”);
exit(1);
}
if ((ifp = fopen(argv[1], “r”)) == NULL)
{
printf(“File cannot be opened for reading\n”);
exit(1); /* ungraceful exit */
}
if ((ofp = fopen(argv[2], “w”) == NULL)
{
printf(“File cannot be opened for writing\n”);
exit(2); /* ungraceful exit */
}
while ((c = fgetc(ifp)) != EOF)
fputc(ofp);
fclose(ifp); /* input file is closed */
fclose(ofp); /* output file is closed */
}

© Cranes Varsity V2.1-2017-18 51/ 106


Advanced C & Data Structure www.cranesvarsity.com

fgets : fgets – read a string of characters from the file stream.

the fgets function is an improvement over fscanf and scanf. It reads the entire line of data from a file

SYNOPSIS

#include<stdio.h>

char *fgets(char *s, int size, FILE *stream);

RETURN VALUES
fgets ( ) returns s on success, and NULL on error or end of file, while no characters have been read.

DESCRIPTION
With fgets we have to specify the size “size”, of the buffer pointed to by s. This function reads up through
and including the next new line, but not more than size-1 characters into the buffer. The buffer is
terminated with a null byte. If the line, including the terminating new line, is longer than n-1 only a partial
line is returned, but the buffer is always null terminated.

Note: Another call to fgets will read what follows on the new line.

fputs – outputs a string to a file stream

This function writes a line of characters to a specified file.

SYNOPSIS

#include<stdio.h>

int fputs (const char *s, FILE *stream);

RETURN VALUES:

fputs ( ) returns a non – negative number on success, or EOF on error

DESCRIPTION

fputs ( ) writes a null terminated string s to the specified stream. Note that the null byte at the end is not
written.

© Cranes Varsity V2.1-2017-18 52/ 106


Advanced C & Data Structure www.cranesvarsity.com

String input and output


Character level I/O

C provides (through its libraries) a variety of I/O routines. At the character level, getchar ( ) reads one
character at a time from stdin, while putchar( ) writes one character at a time to stdout.

For example,
/*program prints number of characters and number of newlines*/

#include<stdio.h>
int main( )
{
int c, nc = 0, nl = 0;
while ((c = getchar( )) != EOF)
{
nc++;
if (c == '\n') nl++;
}
printf("Number of characters = %d, number of lines = \%d\n", nc, nl);
return 0;
}

To read/print a string (sequence of characters), C provides %s format specifier.


#include<stdio.h>
#define SIZE 20
int main( )
{
char name[SIZE];
printf(“\n ENTER THE NAME :”);
scanf(“ %s”,name);
printf(“\n THE ENTERED NAME IS %s”,name);
return 0;
}

Output:
ENTER THE NAME: RAM
THE ENTERED NAME IS RAM
ENTER THE NAME : BILL GATES
THE ENTERED NAME IS BILL

Observe that during the second execution, only first name (BILL) is accepted. This is because if scanf
sees a space it assumes to be the end of input. If you want to accept multi-word names use fgets.
Prototype

char *fgets(char *line, int maxline, FILE *fp)

© Cranes Varsity V2.1-2017-18 53/ 106


Advanced C & Data Structure www.cranesvarsity.com

fgets( ) reads a line of text including the newline from fp into the character array line. At most maxline-1
characters will be read. A ‘\0’ is stored after the last character in the buffer.

#include<stdio.h>
#define SIZE 20
int main()
{
char name[SIZE];
printf(“\n ENTER THE NAME :”);
fgets(name,SIZE,stdin);
printf(“\n THE ENTERED NAME IS %s”,name);
return 0;
}

Output:
ENTER THE NAME : BILL GATES
THE ENTERED NAME IS BILL GATES

sscanf

sscanf reads from a string instead of the standard input.

Prototype:

int sscanf(char *string, char *format, arg1, arg2, ...)

#include<stdio.h>
#define SIZE 20
int main()
{
char date[SIZE];
int dd,mm,yy;
printf(“ENTER THE DATE :”);
fgets(date,SIZE,stdin);

if(sscanf(date,”%d %d %d”,&dd,&mm,&yy) != 3)
printf(“INVALID DATE\n”);
else
{
printf(“ VALID DATE \n“);
printf(“ Day = %d Month = %d Year = %d”,dd,mm,yy);
}
return 0;
}
sprintf

The function sprintf does the same operations as printf does, but stores the output in a string:

Prototype:
© Cranes Varsity V2.1-2017-18 54/ 106
Advanced C & Data Structure www.cranesvarsity.com

int sprintf(char *string, char *format, arg1, arg2, ...);

sprintf formats the arguments in arg1, arg2, etc., according to format as before, but places the result in
string instead of the standard output; string must be big enough to receive the result.

#include<stdio.h>
#define SIZE 20
int main()
{
char regno[SIZE];
int rollno,yearofadmission;
char branch_code[3],college_code[3];
printf(“ENTER THE COLLEGE CODE :”);
scanf(“%2s”,college_code);
printf(“ENTER THE BRANCH CODE :”);
scanf(“%2s”,branch_code);
printf(“ENTER THE YEAR OF ADMISSION :”);
scanf(“%2d”,&yearofadmission);
printf(“ENTER THE ROLL NO :”);
scanf(“%3d”,&rollno);
sprintf(regno,”%s%02d%s%03d”,college_code,yearofadmission,\
branch_code, rollno);
printf(“THE REGNO = %s \n“,regno);
return 0;
}
NAME fread, fwrite – binary stream input/output

SYNOPSIS

#include<stdio.h>

size_t fread (void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream);

RETURN VALUES
Number of objects/items of data read or written.

DESCRIPTION
The function fread reads nmemb elements of data, each size bytes long, from the stream pointed to by
stream, storing them at the location given by ptr. The function fwrite writes nmemb elements of data (from
buffer pointed to by ptr), each size bytes long, to the stream pointed to by stream. These two functions
are used for binary I/O.

Positioning a Stream

NAME ftell
SYNOPSIS

© Cranes Varsity V2.1-2017-18 55/ 106


Advanced C & Data Structure www.cranesvarsity.com

long ftell (FILE *stream);

RETURN VALUE
Current file position indicator if OK, - IL on error.

DESCRIPTION
The ftell function obtains the current value of the file position indicator for the stream pointed to by stream.

NAME fseek – reposition


SYNOPSIS
int fseek(FILE *stream, long offset, int whence);

RETURN VALUES

0 if OK, nonzero on error.

DESCRIPTION
The fseek function sets the file position indicator for the stream pointed to by stream. The new position,
measured in bytes, is obtained by adding offset bytes to the position specified by whence. whence can
have following values:

SEEK_SET -- offset from the beginning of file

SEEK_CUR -- offset from current position of file

SEEK_END -- offset from the end of file

Note: offset can have negative value also.

fprintf and fscanf functions

The fprintf and fscanf functions are provided to perform the analogous operations of the printf and scanf
functions on a file. These functions take an additional argument, which is the file pointer that identifies the
file to which data is to be written or from which data is to be read.

SYNOPSIS

#include<stdio.h>

int fprintf(FILE *stream, const char *format, …);

int fscanf(FILE *stream, const char *format, …);

RETURN VALUES

© Cranes Varsity V2.1-2017-18 56/ 106


Advanced C & Data Structure www.cranesvarsity.com

fprintf returns number of characters outputted if OK and a negative value on error. fscanf returns number
of input items assigned, EOF if input error or end of file before any conversion

DESCRIPTION
These functions handle formatted I.O on file streams.

fprintf( ) function writes the output under the control of a format string that specifies how subsequent
arguments are converted for output.

Note: fprintf functions return the number of characters printed, not including the trailing ‘\0’ used to end
output to strings.

The fscanf function scans input stream according to the specified format. So to write a character string “I
love to work in C\n” in to the file identified by ofp, we can write the statement

fprintf(ofp, “Cranes varsity\n”);

Similarly, to read the string from the file identified by ofp into the variable string, the statement is,

fscanf(ofp, “%s”, string);

could be used.

stdin, stdout, stderr

Usually three “files” are automatically opened by the system for use by the program. These files are
identified by the constant file pointers stdin, stdout, and stderr, which are defined in standard I/O include
file. The file pointer stdin identifies the standard input of the program and is normally associated with our
terminal. All standard I/O functions that perform input and do not take a file pointer as an argument, get
their input from stdin. For ex., the scanf function reads its input from stdin without the file pointer stdin as
the argument and a call to this function is equivalent to a call to the fscanf function with stdin as the first
argument. So the call

fscanf(stdin, “%d”, &i); (or)

scanf(“%d”, &i);

will read in the next integer value from the standard input, which will normally be our terminal.

The file pointer stdout refers to the standard output, which is normally also associated with our terminal.
So a call such as

printf(“I love cranes varsity\n”);

is equivalent to fprintf function with stdout as the first argument,

fprintf(stdout, “I love cranes varsity\n”);

© Cranes Varsity V2.1-2017-18 57/ 106


Advanced C & Data Structure www.cranesvarsity.com

The file pointer stderr identifies the standard error file. This is where the most of the error messages
produced by the system are written, and is also normally associated with our terminal. The reason why
stderr exists is so that error messages can be logged to the device or file other than that where the
normal output is written. This is particularly desirable whenever the program’s output is redirected to a file.
In such a case, the normal output will be written in to the file but any system error messages will still
appear on the terminal. We might want to write our own error messages to stderr for this same reason.

For ex., the fprintf call in the following statement,

if ((fp = fopen(“cranes”, “r ”)) == NULL)


{
fprintf(stderr, “File cannot be opened for reading\n ”);

……………….

will write the indicated error message to stderr if the file cranes cannot be opened for reading. And if the
standard output had been redirected to a file then this message would still appear at the terminal.

/* program to view the working of fprintf, fscanf, fgets, fputs */

#include <stdio.h>
#define SIZE 20
int main( )
{
char string1[SIZE] = "Cranes Varsity\n";
char string2[SIZE];
char string3[SIZE];
FILE *fp;
/* open a file in read & write mode */
if ((fp = fopen("cranes", "r+")) == NULL)
{
fprintf(stderr, "Enter properly\n");
exit(1);
}
fprintf(fp”, %s”,string1);/*Write the string to the file */
/* reset the file pointer to start of the file */
if (fseek(fp, 0, SEEK_SET) < 0)
{
fprintf(stderr, "Not able to seek the file\n");
exit(1);
}
/* scan from the file and store it in the string */
if (fscanf(fp, "%s", string2) <= 0)
{
fprintf(stderr, "fscanf fails\n");
exit(1);
}

© Cranes Varsity V2.1-2017-18 58/ 106


Advanced C & Data Structure www.cranesvarsity.com

printf("%s\n", string2);
if (fseek(fp, 0, SEEK_SET) < 0)
{
fprintf(stderr, "Not able to seek the file\n");
exit(1);
}
if (fgets(string3, 20, fp) == NULL)
{
fprintf(stderr, "fgets fails\n");
exit(1);
}
printf("%s\n", string3);
fclose(fp);
}

Example: Program to concatenate two files


#include <stdio.h>
void filecopy (FILE *, FILE *); // Prototype
int main(int argc, char *argv [ ])
{
FILE *fp;
if (argc == 1) /* no arguments */
filecopy (stdin, stdout);
else
{
while (--argc > 0)
{
if ((fp = fopen(*++argv, “r”) == NULL)
{
printf(“Can’t open %s file\n”, *argv);
exit(1);
}
else
{
filecopy(fp, stdout);
fclose(fp);
}
}
}
}
/* filecopy: copy file ifp to ofp */
void filecopy (FILE *ifp, FILE *ofp)
{
int c;
while ((c = getc(ifp)) != EOF)
putc(c, ofp);
}

Example: Program to read the contents of a file in reverse order


© Cranes Varsity V2.1-2017-18 59/ 106
Advanced C & Data Structure www.cranesvarsity.com

#include <stdio.h>
#include <stdlib.h>
/* program to read the contents of a file in reverse order */
int main(int argc, char *argv[ ])
{
char ch;
FILE *fp;
Int n_chars;
if ((fp = fopen(argv[1], "rb")) == NULL)
{
fprintf(stderr, "\nERROR OPENING IN FILE\n");
exit(1);
}
fseek(fp,0,SEEK_END);
n_chars = ftell(fp);
fseek(fp,-1,SEEK_END);
while(n_chars != 0)
{
ch = fgetc(fp);
putchar(ch);
fseek(fp,-2,SEEK_CUR);
n_chars--;
}
fclose(fp);
return 0;
}

feof : to check for end of file

SYNOPSIS:
int feof (FILE *stream);

DESCRIPTION:
The function feof tests the end-of-file indicator for the stream pointed to by stream, returning non-zero if it
is set.

ferror: to check for error.

SYNOPSIS:
int ferror (FILE *stream);
The function ferror tests the error indicator for the stream pointed to by stream, returning non-zero if it is
set.

Note: rewind

void rewind(FILE * stream);

The rewind function sets the file position indicator for the stream pointed to by stream to the beginning of
the file. It is equivalent to:

© Cranes Varsity V2.1-2017-18 60/ 106


Advanced C & Data Structure www.cranesvarsity.com

fseek(stream, 0L, SEEK_SET) ;

except that the error indicator for the stream is also cleared (see clearerr(3)).

Program to demonstrate fread and fwrite

#include <stdio.h>
#include <stdio_ext.h>
struct Student
{
char name[20],regno[11];
float marks;
int sem;
};
int main(int argc,char **argv)
{
char ch;
FILE *fp = fopen(argv[1],"r+b");
struct Student s;
if(fp == NULL)
{
fprintf(stderr,”UNABLE TO OPEN FILE “);
exit(1);
}
do
{
printf("\n ENTER THE NAME,REGNO, MARKS, SEM : ");
scanf("%s %s %f %d",s.name,s.regno,&s.marks,&s.sem);
fwrite(&s,sizeof(struct Student),1,fp);
printf("\n DO you want to add more records :");
__fpurge(stdin);
ch = getchar( );
}while(ch != 'n');
fflush(fp);
printf("\n BEFORE %d ",ftell(fp));
rewind(fp);
printf("\n AFTER %d",ftell(fp));
printf("\n THE RECORDS ENTERED ARE \n");
printf("\n NAME REGNO MARKS SEM ");
do
{
fread(&s,sizeof(struct Student),1,fp);
fprintf(stdout,"%20s %11s %5.2f, %3d", s.name, s.regno, s.marks,
s.sem);
fflush(fp);
}while(!feof(fp));

© Cranes Varsity V2.1-2017-18 61/ 106


Advanced C & Data Structure www.cranesvarsity.com

Program to demonstrate random file access

#include <stdio.h>
struct Student
{
char name[20],regno[11];
float marks;
int sem;
};
int main(int argc,char **argv)
{
int rec_no,ch;
FILE *fp = fopen(argv[1],"r+b");
struct Student s;
if(fp == NULL)
return ;

do
{
printf("ENTER THE RECORD NO : ");
scanf("%d",&rec_no);
fseek(fp,((rec_no - 1)* sizeof(struct Student)),SEEK_SET);
fread(&s,sizeof(struct Student),1,fp);
printf(" %s %s %f %d\n",s.name,s.regno,s.marks,s.sem);
printf("Do you want to continue : ");
scanf("%d",&ch);
}while(ch != 0);
}

© Cranes Varsity V2.1-2017-18 62/ 106


Advanced C & Data Structure www.cranesvarsity.com

CHAPTER 5
LINEAR DATA STRUCTURES
Introduction to Data Structure

The study of data structure is fundamental to computer science and engineering. Knowledge of data
structures is essential for us to design and develop computer programs that utilize computer resources
(such as, memory) in an effective manner. As you have learnt earlier, data are represented by data
values held temporarily within program’s data area or recorded permanently on a file. Often the different
data values are related to each other.

To enable programs to make use of these relationships, these data values must be in an organized form.
The organized collection of data is called a data structure. Data structures deal with the study of how
the data is organized in the memory, how efficiently it can be retrieved and manipulated and the possible
ways in which different data items are logically related.

Data structures are classified as linear and non-linear data structures. In linear data structure the data
items are arranged in a linear sequence like in an array. An example of a linear data structure is Stack,
Queue. In a non-linear data structure, the data items are not in sequence. An example of a non linear
data structure is a tree, graph.

Data structures may also be classified as homogenous and heterogeneous data structures. An Array is a
homogenous structure in which all elements are of same type. In heterogeneous structures the elements
may not be of the same type. Records are common example of heterogeneous data structures.

These data structures can be implemented as static or dynamic data structures. Static structures are
ones whose sizes are known at compile time. Dynamic structures are ones which expand or shrink as
required during the program execution.

Stack

30
Elements are Insertedspand
Deleted from the same
end 20

10

Figure: 1

A Stack is a linear data structure in which data is inserted and deleted at one end (same end) called the
top. The other end of the stack is called as bottom. Here, the last element inserted will be on top of stack.
Since deletion is done from the same end, last element inserted is the first element to be deleted. So,
stack is also called Last In First Out (LIFO) data structure.

The two basic operations that can be performed on stacks are

© Cranes Varsity V2.1-2017-18 63/ 106


Advanced C & Data Structure www.cranesvarsity.com

 push – to insert an element in to a stack


 pop – to delete an element in to a stack

Stack Implementation

Stack can be implemented as one of the following data structures:

- Static arrays
- Array

Dynamic arrays
- Linked List

Stack Implementation on static array

 The simplest way to represent a stack is by using a one-dimensional array, say stack [N] with
room for N elements. The first element in the stack will be at stack[0], the second element at
STACK[1], and so on.
 An associated variable sp (stack pointer) points to the top element of stack.
 We can implement two styles where, to begin with sp can either be
o sp = -1 or sp = 0
 Therefore the stack full condition would be sp = N-1 or sp = N respectively.
 Similarly the stack empty condition would be sp = -1 or sp = 0 respectively.
 Now, depending upon the sp value the push and pop operations vary
 when sp = 0; push operation  post increment store; pop operation  pre decrement read
 when sp = -1; push operation  pre increment store; pop operation  post decrement read

/*Algorithm to implement stack program*/

Push
 Check for STACK FULL.
 If STACK FULL THEN
 Display Stack Overflow
ELSE
 Read the element to Push
 Place the element at position sp.
 Increment sp

Pop
 Check for STACK EMPTY.
 If STACK EMPTY THEN
 Display Stack Underflow
ELSE
 Decrement sp
 Read the last pushed element
© Cranes Varsity V2.1-2017-18 64/ 106
Advanced C & Data Structure www.cranesvarsity.com

Applications of stack

Stacks are very popular data structure used in various algorithms and applications. Program stack is
implemented based on stack data structure. They are used in Operating System Internals like memory
management system; and also in Conversion of Expression; Evaluation of expression etc.

So far we have seen monolithic programs; henceforth we will write applications with modularity approach.
Why modularity approach?

Because we do not want structural code to be mixed up with user data! The idea is to introduce
modularity and reusability and also, in turn get hold of good programming practices.
In general,

Library layer

 Is called, the lower layer or library module.


 The library layer must contain all the structural code and must be developed first.
 It takes care of all the intricacies like, the stack generation, data storage and retrieval.
 It should not interact with the user directly.
 Must support any number of applications with simple linking.

Application layer

 Is called, the upper layer or application module.


 We should be able to develop applications that require multiple data structures (say, stacks)
without even recompiling the library module.
 The application layer must be aware of the data type.
 Must have proper user interface (UI).
 All communications from the application layer to library layer is via the procedure invocations or
macro invocations so that inner working detail of library is hidden from the application.

Header file

 Is useful for every layer.


 Can be part of upper as well as lower layer.
 It should contain
o Function prototypes,
o Macro definitions: that are frequently used
o Structure declarations: for structures that have to become part of the program.

© Cranes Varsity V2.1-2017-18 65/ 106


Advanced C & Data Structure www.cranesvarsity.com

Header file for stack

/*************************************************************************************************
Project : Implementation of stack on static arrays
Module : Header file – stk.h
Description : Library layer. Includes STD headers function prototypes, symbolic
literals and enum constants
*************************************************************************************************/
#include <stdio.h>
/* Function prototypes */
int push (int item);
int pop (void);
int pisplay (void);

/* some useful macros */


#define NITEMS 10
#define S_FULL -1
#define S_EMPTY -2
#define S_SUCCESS 0

/* defining enumeration constants */


enum operations {PUSH = 1, POP, DISP, EXIT};

/***** End of stk.h*****/

/* *************************************************************************************************
Project : Implementation of stack on static arrays
Module : library module – stk_lib.c
Description : Includes all the required function Definitions, header files, globals
**************************************************************************************************/
#include “stk.h”
static int stack[NITEMS];
static int sp = -1;
/*************************************************************************************************/
Function Name : push
Purpose : push item onto stack
Parameters : data item
return value : returns SUCCESS if successful

*************************************************************************************************/
int push(int item)
{
if (sp >= (NITEMS – 1))
return S_FULL;
else /* preincrement the sp and then insert */
stack [++sp] = item;
return S_SUCCESS;
}

© Cranes Varsity V2.1-2017-18 66/ 106


Advanced C & Data Structure www.cranesvarsity.com

/* ****************************************************** *******************************************
Function Name : pop
Purpose : pop item from the stack
Parameters : (describe each argument, if req.)
Return value : return data item that is poped
* ****************************************************** ******************************************/
int pop(void)
{
int data;
if (sp == -1)
return S_EMPTY;
else /*Pop the element and then decrement
post decrement) sp*/
data = stack [sp--];
return data;
}

/* ************************************************************************************************
Function Name : display
Purpose : display the remaining items in the stack
Parameters : (describe each argument, if req.)
Return value : data item to be displayed
**************************************************************************************************/
void display(void)
{
int i;
for(i = sp; i >= 0; i--)
printf(“ %d\n”, stack[i]);
}

/***** End of stk_lib.c *****/

/* *************************************************************************************************
Project : Implementation of stack on static arrays
Module : application module – stk_app.c
Description : A User Interface that performs different Operations on stack and depends
on Header file and library module
***************************************************************************************************/
#include ”stk.h”

/* ****************************************************** ******************************************
Function Name : main
Purpose : invokes the functions from library layer for performing specific operations
Parameters : (describe each argument, if req.)
Return value : (explain if there is any specf. Reason)
* *********************************************************************************************** */
int main( )
{
int ch, item, i;
© Cranes Varsity V2.1-2017-18 67/ 106
Advanced C & Data Structure www.cranesvarsity.com

while(1)
{
printf(“\n 1.PUSH \n 2.POP \n 3.DISPLAY \n4.EXIT\n”);
printf(“\n Enter your choice :”);
scanf (“%d”,&ch);
switch (ch)
{
case PUSH:
printf(“Enter the element to insert: “);
scanf (“%d”,&item);
if (push(item) == S_FULL)
printf(“Stack full\n”);
break;

case POP:
if ((item = pop( )) == S_EMPTY)
printf(“Stack empty\n”);
else
printf(“popped item = %d\n”, item);
break;

case DISP:
display ( );
break;

case 4:
default:
exit (0);
}
}
return 0;
}
/***** End of stk_app.c *****/

Evaluating an expression using Stack

We can use stacks to evaluate expressions in postfix notation. The steps to be followed to evaluate the
postfix expression are:
 Scan from left to right. When an operand is encountered, push to the stack.
 When an operator is encountered, Pop two elements from the stack and perform the operation
based on the operator
 Push the result back to stack
 Repeat the above steps till you reach the end of input expression.
 At the end, there will be one element in the stack, which is the result.

Consider the expression


8 5 3 + 9 * + 4 + is evaluated as follows.

© Cranes Varsity V2.1-2017-18 68/ 106


Advanced C & Data Structure www.cranesvarsity.com

We can also use stacks to evaluate expressions in postfix notation. To do this, when an operand is
encountered, it is pushed on to the stack, when an operator is encountered, it is applied to the first two
operands that are obtained by popping the stack and die result is pushed onto the stack. For example,
the postfix expression.8 5 3 + 9 * + 4 + is evaluated as follows.

On reading 8, 5 and 3, the stack contents are


3
5 5
8 8 8

On reading +, 3 and 5 are popped out and are added; the result 8
(3+5) is pushed onto stack

8
8
Next push 9
9
8
On reading *, 9 and 8 is popped and multiplied, the 8
result 72 (8 * 9) is pushed to stack
72
On reading + 72 and 8 are popped out and are added; 8
the result 80 (8+72) is pushed onto stack
4
Now 4 is pushed to stack 80

Finally + is read and 4 and 80 is popped and added


and the result 84 is pushed back 84

End of the string encountered there is one element in the stack which is popped out and that is the result,
i.e. 84.

/* ************************************************************************************************ *
Project : Stack usage – evaluation of suffix expr
Module : application module – suffix.c
Description : An application that evaluates suffix expr using stack And depends on
header file and Library module
* ****************************************************** *******************************************/
#include <stdio.h>
#include <ctype.h>
#include “stk.h”

#define SIZE 30

/* ****************************************************** ******************************************
Function Name : main
Purpose : invokes the functions for performing specific operations
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. Reason)
© Cranes Varsity V2.1-2017-18 69/ 106
Advanced C & Data Structure www.cranesvarsity.com

* ****************************************************** ******************************************/
int main(void)
{
char suffix [SIZE];
int val,op1,op2,res;
int i;

printf(“ Enter a valid suffix expression “);

fgets(suffix, 50, stdin) ;


for(i = 0 ; suffix[i] != ‘\0’ ; i++)
{
if (isalpha(suffix[i]))
{
printf(“\n Enter the value of %c :”, suffix[i]);
scanf(“%d”,&val);
push (val);
}
else
{
op2 = pop ( );
op1 = pop ( );
switch (suffix[i])
{
case ‘+’: res = op1 + op2; break;
case ‘-‘: res = op1 – op2; break;
case ‘*’: res = op1 * op2; break;
case ‘/’: res = op1 / op2; break;
}
push(res);
}
}
printf(“\n The result of the expression is %d”,pop( ));
return 0;

Conversion of Expression

An expression consists of operators and operands. The operator specifies the kind of operation to be
performed on operands. A simple binary arithmetic expression consists of two operands and one
operator.
e.g., a + b
where a and b are operands and + is an operator. Here the operator appears in between the two
operands. This type of writing expression is called an infix expression.
There are other alternate ways of representing the expression a + b. These are a b + which is called
postfix (suffix or reverse polish) expression and + a b which is called a prefix (polish) expression.

© Cranes Varsity V2.1-2017-18 70/ 106


Advanced C & Data Structure www.cranesvarsity.com

In general, any expression having an operator in between two operands is called an infix expression. An
expression where an operator follows the two operands is a postfix expression. An expression where an
operator precedes the two operands is a prefix expression.
Following examples show several arithmetic expressions in infix, prefix and postfix notation.

We can convert infix notation to postfix and prefix notation by using stack data structure.
We follow the following mechanism. Suppose we want to convert the given infix expression into postfix
notation.

A–B+C*D

We read one character at a time.

We may write an algorithm as given below:


1. To begin with the stack is empty.
2 For each character in the infix string
if operand
append to output
else if operator or ‘(‘
if stack empty or the operator has higher precedence
than the operator on top of the stack then

© Cranes Varsity V2.1-2017-18 71/ 106


Advanced C & Data Structure www.cranesvarsity.com

push it onto the stack


else
pop the stack and append it to the output and then place the element
if ‘)’ is encountered
pop the elements till the opening braces (
and output.
Remove the (and do not place in the output.
3. If input end encountered then loop until stack not empty
pop the stack and append to the output.

/***************************************************************************************************
Project : Stack usage – conversion of given infix expr to suffix expr
Module : header file – infix_suffix.h
Description : Includes all the required function prototypes, symbolic literals, etc.
***************************************************************************************************/
#include <stdio.h>
#include<stdbool.h>
#define S_SUCCESS 0
#define S_FAIL -1
#define S_EMPTY -2
#define S_FULL -3

#define SIZE 20

/* function prototypes */

bool precedence (char x);


int pop(void);
bool push(char x);

/* ****************************************************** *******************************************
Project : Stack usage – conversion of given infix expr to suffix expr
Module : library module – infix_suffix_lib.c
Description : Includes all the required function Definitions and header files
* ****************************************************** ******************************************/
/* ****************************************************** *******************************************
Function Name : precedence
Purpose : check the precedence of the operators in the given expr.
Parameters : data item
return value : (explain if there is any specf. Reason)
* *********************************************************************************************** */
#include “infix_suffix.h”
char infix [SIZE], suffix [SIZE], stack [SIZE];
int sp = -1;

bool precedence (char x)


{
int i, j, popd;
while ((x==’+’ || x==’-‘) && (stack[sp] ==’+’ ||
stack [sp]==’-‘ || stack[sp]==’*’ ||
© Cranes Varsity V2.1-2017-18 72/ 106
Advanced C & Data Structure www.cranesvarsity.com

stack [sp]==’/’) && sp >= 0)


if((popd = pop( )) == S_EMPTY)
return S_FAIL;
else
Suffix [j++] = popd;
while((x == ‘*’ || x == ‘/’) &&
(stack[sp] == ‘*’ ||
stack[sp] == ‘/’) && sp >= 0)
if((popd = pop( )) == S_EMPTY)
return S_FAIL;
else
suffix [j++] = popd;
return S_SUCCESS ;
}
/* ****************************************************** ******************************************
Function Name : pop
Purpose : pop item from the stack
Parameters : (describe each argument, if req.)
return value : return data item that is poped
* ****************************************************** ******************************************/
int pop(void)
{
if (sp == -1)
return S_EMPTY;
else
return stack [sp--];
}

/* *************************************************************************************************
Function Name : push
Purpose : push item onto stack
Parameters : data item
return value : (explain if there is any specf. Reason)
* ****************************************************** ******************************************/
bool push(char x)
{
if (sp >= SIZE – 1)
return S_FULL;
stack [++sp] = x;
return S_SUCCESS;
}

/* ****************************************************** *****************************************
Project : Stack usage – conversion of given infix expr to suffix expr
Module : application module – infix_suffix_app.c
Description : A User Interface that converts expr From infix to suffix using stack
and depends on header file and Library module

* *********************************************************************************************** */
/* ****************************************************** *******************************************
© Cranes Varsity V2.1-2017-18 73/ 106
Advanced C & Data Structure www.cranesvarsity.com

Function Name : main


Purpose : invokes the functions from library layer for performing specific operations
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. Reason)
* ****************************************************** */
#include “infix_suffix.h”
extern char infix [SIZE], suffix [SIZE], stack [SIZE];
extern int sp;
int main( )
{
int i, j;
printf(“ Enter a valid infix expression “);
if (fgets(infix, 50, stdin) == NULL)
{
fprintf(stderr,”fgets failed”);
exit (1);
}
for (i = 0 ; infix[i] != ‘\0’ ; i++)
{
switch (infix[i])
{
case ‘+’: case ‘-‘:
case ‘*’: case ‘/’:
case ‘$’: case ‘(‘:
precedence (infix[i]);
if(push(infix[i] != S_SUCCESS)
printf(“Stack Full\n”);
break;
case ‘)’:
while (stack[sp] != ‘(‘)
if((ch = pop( )) == S_EMPTY)
printf(“Stack Empty\n”);
else
suffix[j++] = ch
if((ch = pop( )) == S_EMPTY)
printf(“Stack Empty\n”);
break;
default:
suffix [j++] = infix[i];
}
}
suffix [j++] = pop ( );
printf(“\n The suffix expression is %s”,suffix);
return 0;
}

****************************************************************************************************
Project : Stack usage – conversion of given infix expr to suffix expr
Module : Makefile
Description : Execution script
© Cranes Varsity V2.1-2017-18 74/ 106
Advanced C & Data Structure www.cranesvarsity.com

***************************************************************************************************
#make file for the execution of conversion application
INFIX_SUFFIX: infix_suffix_lib.o infix_suffix_app.o
gcc –o INFIX_SUFFIX infix_suffix_lib.o infix_suffix_app.o
stk_lib.o: infix_suffix_lib.c infix_suffix.h
gcc –c infix_suffix_lib.c
stk_app.o: infix_suffix_app.c infix_suffix.h
gcc –c infix_suffix_app.c
clean:
rm *.o

Usage of Stack in Recursion

Stack is used during recursion. When a function is called, the return address, the values of local variable
and formal parameters will be pushed on to the stack. Then control enters into the function. Once the
return statement is executed, control comes back to the same point from where it had been called and the
returned value is substituted at that point and the control goes to the statement, which immediately
follows the function call. To explain how actually the stack is used using recursion, consider the below
program which prints the given string in reverse order.

/* ****************************************************** ******************************************
Project : Stack construction in a recursive function call
Module : application module – recurse.c
Description : A User Interface that illustrates stack construction
* *********************************************************************************************** */

/* ****************************************************** ******************************************
Function Name : rev
Purpose : reverses a given string when invoked Recursively
Parameters : (describe each argument, if req.)
Return value : (explain if there is any specf. Reason)
* ***************************************************************************************************************** */
#define SIZE 50

void rev (char *a)


{
if(*a == ‘\0’)
return;
rev (a+1);
printf(“%c”,*a);
}
/* ********************************************************************************************** *
Function Name : main
Purpose : invokes the functions for performing specific task
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. Reason)
* ******************************************************************************************************************* */
int main( )
{
char str[SIZE];
© Cranes Varsity V2.1-2017-18 75/ 106
Advanced C & Data Structure www.cranesvarsity.com

int len;

printf(“ Enter a string : “);


if ((fgets(str, SIZE, stdin)) == NULL)
{
fprintf(stderr,”fgets failed”);
exit (1);
}
len = strlen(str) – 1;
str[len] = ‘\0’;
printf(“\n The entered string is %s”,a);
printf(“\n The reverse of the string is “);
rev (str);
printf(“\n”);
return 0;

Queue

A Queue is a linear data structure in which data is inserted at one end called the rear and deleted at other
end called the front. Here, the first element inserted will be the first element to be deleted. So, Queue is
also called First In First Out (FIFO) data structure. It contrasts with stacks, which are last in first out
(LIFO)

Front End Rear End

© Cranes Varsity V2.1-2017-18 76/ 106


Advanced C & Data Structure www.cranesvarsity.com

Queues are part of our everyday life. We find queues at ticket counters, banks etc. A good example for
queues in computer science occurs in a multitasking system, where the scheduling of jobs takes place.
Suppose there are five programs waiting to be executed. They are put in a queue, and if the sixth
program is to be executed, it is put at the end of the queue. The program that is at the beginning of the
queue gets executed first.

The basic operations that can be performed on Queue are


 insertion
 deletion

There are various queue data structures, viz,


 Linear queues
 Circular queues
 Priority queues

Queue Implementation:

Queue can be implemented as one of the following data structures:


Static
 Array

 Linked List Dynamic

Algorithm for Inserting and Deleting in a Queue

Insert
 Check for queue full.
 If queue full then
Display queue overflow
Else
Place the element at position rear.
Increment rear
Delete
 Check for queue empty.
 If queue empty then

Display Queue Underflow


Else
Delete Front element
Increment Front.

Priority Queues

Many application involving queues require priority queues rather than the simple FIFO strategy. Each
queue element has an associated priority value and the elements are served on a priority basis instead of
using the order of arrival. For elements of same priority, the FIFO order is used.

© Cranes Varsity V2.1-2017-18 77/ 106


Advanced C & Data Structure www.cranesvarsity.com

For example, in a multi-user system, there will be several processes competing for use of the processor
at one time. The processes have a priority value associated to them and are held in a priority queue. The
process with the highest priority is first executed by the processor.

Limitations of Static Arrays

In the previous section, we have studied Stack and Queue and its implementation using static array.
Static arrays have certain limitations; the size of the array must be fixed at compile time, it cannot grow or
shrink at runtime. This limitation can be overcome by implementing Stack and Queue on dynamic array;
but still the approach of stack and queue itself restrict insertion and deletion so that they can take place
only at the beginning or the end of the data list and not in the middle.

Another disadvantage with arrays as such is that, if we have to insert an element at the i th locations, all
the elements from ith location till the end of the array, have to be moved to the next subsequent locations
to make room for the element to be inserted.

Similarly, to delete an element at ith location, all the elements from (i+1)th location have to be moved to
their corresponding previous locations. So, if the applications require extensive manipulation of stored
data, in the sequential representation (arrays), more time is spent in the movement of data.

To overcome these drawbacks, we use dynamic memory allocation and linked representation of data.

Linked list

The primary advantage of Linked Lists over arrays is that Linked Lists can grow and shrink in size during
runtime. Their maximum size need not be known in advance.

In practical applications, this often makes it possible to have several data structures share the same
space, without paying particular attention to their relative size at any time.

A second advantage of Linked Lists is that they provide flexibility in allowing the items to be rearranged
efficiently.

What Linked Lists Look Like

An array allocates memory for all its elements lumped together as one block of memory.
In contrast, a linked list allocates space for each element separately in its own block of memory called a
“node”. Thus we have pointers to connect all the nodes of the list together like the links in a chain, hence

© Cranes Varsity V2.1-2017-18 78/ 106


Advanced C & Data Structure www.cranesvarsity.com

the name. Each node contains two fields: an “information” field to store the data and a “next” field which is
a pointer used to link one node to the next node. Each node is allocated in the heap with a call to malloc(
), so the node memory continues to exist until it is explicitly deallocated with a call to free( ). The front of
the list is a pointer to the first node. The pointer field of last node contains a special value, called NULL
pointer.
Linked lists in C are represented using self referential structures in the following manner –
A node in the list can be represented as follows.
Struct node
{
int info;
struct node *nxt;
};

There are various linked list data structures


 Singly linked list
 Doubly linked list
 Circular linked list

/*Slist.h: header file of singly linked lists*/


/* ****************************************************** *******************************************
Project : Singly linked list – Implementation
Module : header file – Slist.h
Description : Includes all the required structure definitions, symbolic literals and
function prototypes
* ************************************************************************************************ */
struct node
{
int info;
struct node *nxt;
};
typedef struct node NODE;

/*some useful macros*/


#define L_SUCCESS 0
#define L_FAIL -1
#define L_NOT_FOUND -2
#define L_EMPTY -3

/*function declarations*/
NODE * create_node (int element) ;
int insert_front (int element) ;
int append(int element) ;
© Cranes Varsity V2.1-2017-18 79/ 106
Advanced C & Data Structure www.cranesvarsity.com

int traverse(void) ;
int search (int key);
int modify (int key, int newData);
int delete_front (void);
int delete_rear (void);
int delete_specified (int Key);
/*end of list.h*/
Operations on Singly Linked Lists
The operations that can be performed on singly linked lists are listed below –
 Insertion/appending of nodes
 Traversal on the list
 Search for a specific node
 Modification of a node
 Deletion of a node

/* Slist_lib.c: library module of linked lists */


Creation of new node:

 Create the new node by dynamically allocating some memory


 Set its data field to element which needs to be stored.
 Set the nxt pointer of the new node to NULL.
/* ****************************************************** *****************************************
Project : Singly linked list – Implementation
Module : library module – Slist_lib.c
Description : Includes all the required function Definitions
****************************************************** *******************************************/
#include “Slist.h”
NODE *front;
/* ****************************************************** *
Function Name : create_node
Purpose : creates a node dynamically
Parameters : (describe each argument, if req.)
return value : returns new node’s address
* ****************************************************** */

NODE * create_node(int element)


{
NODE * newNode;
newNode = (NODE *) malloc (sizeof(NODE));
if (newNode == NULL)
return NULL;
newNode->info = element;
newNode->nxt = NULL;
return newNode;
}

Insertion of a node at the beginning of the List

These 3 steps have to be followed,


© Cranes Varsity V2.1-2017-18 80/ 106
Advanced C & Data Structure www.cranesvarsity.com

 Create the node by invoking the create function


 Insertion: Set the “nxt” pointer of the new node to the current
first node of the list. This is just, a pointer assignment.
Note: assigning one pointer to another makes them point to the same memory.
NewNode->nxt = front;
 Now set the “front” pointer to the new node, so that it is
the first node in the list.
Front = newNode;

/* ****************************************************** ******************************************
Function Name : insert_front
Purpose : inserts a new node at the front of the singly linked list
Parameters : first node’s address and the data to be inserted.
Return value : returns success status
* ****************************************************** ****************************************/
int insert_front(int element)
{
NODE *newNode;
newNode = Create_Node (element);
if (newNode == NULL)
return L_FAIL;

newNode->nxt = front; /*will always be the first node*/


front = newNode;
return L_SUCCESS;
}

Appending of a node into the list

These 2 steps have to be followed,


 Create the node by invoking the create function
 Append: Set the “nxt” pointer of the new node to NULL as it will always be the last node
newNode->nxt = NULL;

© Cranes Varsity V2.1-2017-18 81/ 106


Advanced C & Data Structure www.cranesvarsity.com

/* *********************************************************************************************** *
Function Name : append
Purpose : adds a new node at the end of the singly linked list
Parameters : first node’s address and the data to be inserted.
Return value : returns success status
* ********************************************************************************************** */
int append (int element)
{
NODE *newNode, *temp;
newNode = create_node(element);
if (newNode == NULL)
return L_FAIL;
if (front == NULL) /* If the list is empty,
front = newNode; /* the newly created node is the first node */
else /*if the list contains some node, Traverse till the last node*/
{
temp = front;
while (temp -> next != NULL)
temp = temp ->nxt;
/*Make the nxt of last node point to new node*/
temp->nxt = newNode;
}
return L_SUCCESS;
}

Traversing a Linked List


Once the list is created, the next step is to display the contents of the list. To display the contents of the
list, it has to be read, i.e., traverse node by node until the end of the list is reached. Since it is a singly
linked list, traversal is always
unidirectional. This involves the following steps –
 Set a temporary pointer to the first node, to walk on the list sequentially.
 Display the information part of the node.
 Advance the pointer so that it points to the next node. This can be done by temp = temp->nxt;
 Repeat steps 2 and 3 until the last node is reached.

© Cranes Varsity V2.1-2017-18 82/ 106


Advanced C & Data Structure www.cranesvarsity.com

/* *********************************************************************************************** *
Function Name: traverse
Purpose : traverse through the entire length of the singly linked list
Parameters : first node address
Return value : returns success status
* *********************************************************************************************** */
int traverse(void)
{
NODE* temp;
if (front == NULL)
return L_EMPTY;

temp = front;
while (temp != NULL)
{
printf(“%d\n”, temp->info);
temp = temp->nxt;
}
Return L_SUCCESS;
}

Searching in a Linked List


Searching in a linked list involves traversing the list until the required node is reached. This involves the
following steps
 Key is the value to be searched in the list.
 Set a temporary pointer to the first node.
 Compare the info in the node with the key.
 If they are equal, stop searching and report the position which, it was found at.
 Else, advance to the next node in the list.
 Repeat steps 3, 4 and 5 till the end of the list is reached.

/* ****************************************************** ******************************************
Function Name : search
Purpose : Search for a particular value among all the nodes in the singly linked list
Parameters : first node’s address and the key to Be searched
Return value : returns the position of the value if found
* ************* ***************************************** ****************************************/
int search (int key)
{
int position = 0 ;
NODE* temp = front ;

/*
* Traverse the list till you reach the node with key
* info or till you reach the end of list
*/

© Cranes Varsity V2.1-2017-18 83/ 106


Advanced C & Data Structure www.cranesvarsity.com

while (temp != NULL && temp->info != key)


{
temp = temp->nxt;
position++;
}
if (temp == NULL)
return L_NOT_FOUND;
else
return position;
}

Modification in a List
Modification in a linked list involves –
 Searching for the key node to be modified.
 Changing the information part of the node.

/* ****************************************************** *******************************************
Function Name : modify
Purpose : searches for key node in the list and if found; modifies its value
Parameters : first node’s address, key, newdata
Return value : returns SUCCESS if successful
* ****************************************************** ******************************************/
int modify (int key, int newData)
{
NODE* temp = front;

while (temp != NULL && temp->info != key)


temp = temp->nxt;

if(temp == NULL)
return L_NOT_FOUND;
else
temp->data = newData;

return L_SUCCESS;
}

Deletion in a Singly Linked List


Deletion in a Singly Linked List may involve one of the following situations –
 Deleting a node at the beginning of the list.
 Deleting at the end of the list.
 Deleting a specified node in the list.

Delete Front
For deleting a node at the beginning of a list, the steps involved are –
 Check if the list is empty, if empty stop.
© Cranes Varsity V2.1-2017-18 84/ 106
Advanced C & Data Structure www.cranesvarsity.com

 Set a temporary pointer to the first node.


 Reset pointer “front” to the next node, the would be first node.
 Front = delNode->nxt;
 Free the memory occupied by the node.
Free (delNode);

/* ************************************************************************************************ *
Function Name : delete_front
Purpose : deletes the first node in the list
Parameters : first node’s address
Return value : returns success status
* ****************************************************** ******************************************/
int delete_front (void)
{
NODE* delNode;

if (front == NULL)
return L_EMPTY;
delNode = front; /* Make delNode point to first node */
printf(“%d”, delNode->info);
front = front->nxt; /* Make the front point to second node.*/
free (delNode); /* Deallocate the memory */

Return L_SUCCESS;
}

Delete Rear

For deleting a node at the end of a list, the steps involved are –
1. Check if the list is empty, If empty Stop.
2. If there is only one node, delete the node and set pointer “front” to NULL.
3. To delete the last node when there are n number of elements in the list. Traverse till the
second last node and delete the last node.
4. Set the pointer “nxt” of second last node to NULL.

© Cranes Varsity V2.1-2017-18 85/ 106


Advanced C & Data Structure www.cranesvarsity.com

/******************************************* ****************************************************** *
Function Name : delete_rear
Purpose : deletes the last node in the list
Parameters : first node’s address
Return value : returns success status.
****************************************** ****************************************************** */
int delete_rear(void)
{
NODE * delNode = front,* prev;

if (front == NULL)
return L_EMPTY;

if (front->nxt == NULL)
{
printf(“The deleted element is %d\n”,front->info);
free (front);
front = NULL;
}

else

{
/*
Make delNode to Traverse till delNode reaches the last node
*/
do
{
prev = delNode;
delNode = delNode->nxt;
} while(delNode -> nxt != NULL);

prev->nxt = NULL;
printf(“The deleted element is %d\n”, delNode->info);
free (delNode);
}

© Cranes Varsity V2.1-2017-18 86/ 106


Advanced C & Data Structure www.cranesvarsity.com

return L_SUCCESS;
}

Deleting a specified node in the list

 Traverse the list until the item to be deleted is found.


 When found, before deleting it from the list we need to connect the list on the right hand side of
the node with list on the left hand side of the node.
 Dispose the memory space held by the deleted element.
Free (delNode);

/* ************************************************************************************************ *
Function Name : delete_specified
Purpose : searches for the key node in the list if found deletes it
Parameters : first node’s address, key
Return value : returns success status
* *********************************************************************************************** */

int delete_specified (int Key)


{
NODE *delNode = front, *prev;

if (front == NULL)
return L_EMPTY;
else if (front->info == key)
{
front = front->nxt;
free(delNode);
return L_SUCCESS;
}

/*
Make delNode point to first node
Traverse till you find the specified node
*/
do
{
prev = delNode;

© Cranes Varsity V2.1-2017-18 87/ 106


Advanced C & Data Structure www.cranesvarsity.com

delNode = delNode->nxt;
} while (delNode != NULL && delNode->info != key);

if(delNode == NULL)
return L_NOT_FOUND;

if(delNode ->next == NULL && prev->info == key)


prev ->nxt = NULL;
else
Prev ->nxt = delNode ->nxt;

free(delNode);

return L_SUCCESS;
}

Implementing stack using linked list

A Stack is a restricted access data structure where elements can be inserted and deleted from only one
end. To implement stack using linked list only insert_front, delete_front and display functions are required.

Implementing queue using linked list

A Queue is another restricted access data structure where elements can be inserted from one end and
deleted from other end. To implement queue using linked list only insert_rear, delete_front and display
functions are required.

Doubly linked lists

One major draw back of a singly linked list is that traversal is possible only in one direction. The only way
to access the node preceding the current node is to start traversing from the beginning of the list.

This is because each node has a link that points only to the next node in the list and not to the previous
node. This could prove to be a major hindrance, especially when one has to process a very long list.

A doubly linked list is just an extension of the concept of a singly liked list. Each node in a doubly linked
list has an information part and two pointers – one pointing to the next node and one to the previous
node.

108
108 121 273 342
first

X 01 121 108 02 121 03 342 273 04 X


art 273
L I R L I R L I R L I R

13
© Cranes Varsity V2.1-2017-18 13 1388/ 106
13
Advanced C & Data Structure www.cranesvarsity.com

Here,
L stands for prev
R stands for nxt, and
I stands for Info.

Representing Doubly Linked Lists in C

Doubly Linked Lists are represented in C in the same manner in which singly linked lists are.

Struct node
{
datatype data;
….
Struct node* prev;
Struct node* nxt;
};

Manipulation of Doubly Linked Lists

The operations that can be performed on doubly linked lists are similar to those on singly linked lists.
They are –

 Creation of a list/insertion into a list


 Traversal in a list (both ways)
 Searching for a particular node
 Modification of a node
 Deletion of a node

/*Dlist.h: header file of linked lists*/


/* ************************************************************************************************ *
Project : Doubly linked list – Implementation
Module : header file – Dlist.h
Description : Includes all the required structure definitions, symbolic literals and
function prototypes
* ****************************************************** ******************************************/
struct node
{
int info;
struct node *prev;
struct node *nxt;
};
typedef struct node NODE;

/*some useful macros*/


#define L_SUCCESS 0
#define L_FAIL -1
#define L_NOT_FOUND -2
#define L_EMPTY -3

© Cranes Varsity V2.1-2017-18 89/ 106


Advanced C & Data Structure www.cranesvarsity.com

/*function declarations*/
NODE * Create_Node(int element) ;

int Insert_Begin(int element) ;

int Insert_Key(int item, int key);

int Display (void);


int Delete_F(void);

/*end of Dlist.h*/

/*Dlist_lib.c: library module*/


/* *********************************************************************************************** *
Project : Doubly linked list – Implementation
Module : library module – Dlist_lib.c
Description : Includes all the required function definitions
************************************************************************************************* */
#include “Dlist.h”
NODE *front;

/* ************************************************************************************************ *
Function Name : create_node
Purpose : creates a node dynamically
Parameters : (describe each argument, if req.)
return value : returns new node’s address
* *********************************************************************************************** */
NODE * create_node(int element)
{
NODE * newNode;
newNode = (NODE *) malloc (sizeof (NODE));
if (newNode == NULL)
return NULL;

newNode->info = element;
newNode->prev = NULL;
newNode->nxt = NULL;
return newNode;
}

Insertion in a Doubly Linked List:

© Cranes Varsity V2.1-2017-18 90/ 106


Advanced C & Data Structure www.cranesvarsity.com

Function to insert a node at Front

/* *************************************************************************************************
Function Name : Insert_f
Purpose : inserts a new node at the front of the doubly linked list
Parameters : data to be stored in the new node.
Return value : returns success status if successful
* ****************************************************** ******************************************/
int insert_begin (int item)
{
NODE * newnode;
int item;

newnnode = Create_Node(int item);


if (newnode == NULL)
return L_FAIL;

if(front != NULL)
{
newnnode ->nxt = front;
front->prev = newnnode;
}
front = newnode;
return L_SUCCESS;
}

Inserting a node to the left of the given key node

Function to insert a node to the left of a given key element. Assume 30 is the key element.
/* *********************************************************************************************** *
Function Name : Insert_key
Purpose : inserts a new node to the left of the key in the doubly linked list
Parameters : new node’s data and key value
Return value : returns success status if successful
* ****************************************************** ******************************************/
int insert_key(int item, int key)
{
NODE* newnode, *temp;
© Cranes Varsity V2.1-2017-18 91/ 106
Advanced C & Data Structure www.cranesvarsity.com

newnode = Creat_Node(item);
if (newnode == NULL)
return L_FAIL;
if(key == front->info)
{
newnode->nxt = front;
front->prev = newnode;
front = newnode;
}
else
{
temp = front;
While (temp != NULL && temp->info != key)
temp = temp->nxt;
if(temp != NULL)
{
temp->prev->nxt = newnode;
newnode->prev = temp->prev;
newnode->nxt = temp;
temp->prev = newnode;
}
else
{
free (newNode);
return L_NOT_FOUND;
}
}
return L_SUCCESS;
}

Traversal in a Doubly Linked List

/* *********************************************************************************************** *
Function Name : Display
Purpose : traverses and displays all the node’s content
Parameters : None
Return value : returns success status if successful
* ********************************************************************************************** */
int display(void)
{
NODE temp;
if (front == NULL)
return L_EMPTY;
for (temp=front; temp != NULL; temp = temp->nxt)
printf(„%d\t“,temp->info);
return L_SUCCESS;
}

© Cranes Varsity V2.1-2017-18 92/ 106


Advanced C & Data Structure www.cranesvarsity.com

Deletion in a Doubly Linked List


Delete Front

Function to delete a node at Front

/* ********************************************************************************************** *
Function Name : delete_f
Purpose : deletes the first node in the list
Parameters : None
Return value : returns success status on succesfull.
* ********************************************************************************************* */
int delete_f(void)
{
NODE* delnode;

if (front == NULL)
return L_EMPTY;
delnode = front;
front = front->nxt; /* Make the front pointer point
to second node */
front->prev = NULL; /* null the prev of second node */
printf(“\n The deleted node is %d”, delnode->info);
free (delnode);

return L_SUCCESS;
}

Circular Linked List:

Circular linked list is a sequence of elements in which every element has link to its next element in the
sequence and the last element has a link to the first element in the sequence.

CLL can be applied to both SLL and DLL. The operations that can be performed on CLL are:
1. Insertion,
2. Deletion,
3. Traversal,
4. Searching,
etc…

Given below is the concept of CLL in SLL and DLL.

© Cranes Varsity V2.1-2017-18 93/ 106


Advanced C & Data Structure www.cranesvarsity.com

© Cranes Varsity V2.1-2017-18 94/ 106


Advanced C & Data Structure www.cranesvarsity.com

NON LINEAR DATA STRUCTURE

Introduction to Trees

The term "tree" is used in Computer Science to denote a particular type of abstract data structure.

Definition

A tree is a finite nonempty set of elements. One of these elements is called the root, and the remaining
elements are partitioned into trees which are called the sub trees.

Trees contain data in structures called nodes, which are in turn linked to other nodes in the tree.
Additionally, each node is the parent of sub trees. Every node in a tree has 0 or more children. One or
more of the sub trees may have no elements and are called empty sub trees. In this way a tree can be
described as being made up of multiple sub trees.

root: Every tree has a primary node called a root, from which all other branch nodes in the tree descend.

leaf : The nodes that have no descendents are called leaf nodes.

The level of any node n is given by the number of edges in the path from root node and the level of root
node is zero. The height of a tree is one more than the maximum level in the tree.

Binary tree
Definition

A binary tree is a finite (possibly empty) collection of elements. When a binary tree is not empty, it has a
root element and the remaining elements (if any) are partitioned into two binary trees, which are called left
and right sub trees.

The essential differences between a binary tree and a tree are


 A binary tree can be empty, where as a tree cannot.
 Each element in a binary tree has exactly two sub trees (one or both of these sub trees may be
empty). Each element in a tree can have any number of sub trees.

Binary Search Tree

The most common type of tree is the binary-search tree. In BST, for each node contains a key and
satisfies the conditions.
 The key in the left child of a node is less than the key in its parent node.
 The key in the right child of a node is greater than the key in its parent node.
 The left and right subtrees of the root are again binary search trees.
 No two nodes can have the same key i.e., Duplicates are not allowed in BST.
o Every node in the tree should satisfy these conditions.

© Cranes Varsity V2.1-2017-18 95/ 106


Advanced C & Data Structure www.cranesvarsity.com

Representing Binary Trees in C

Binary Trees are represented in C in much the same way in which linked lists are though the way in which
data is organized in the two data structures is not the same. Each node contains two link parts and one
information part.

One link points to the left child and other points to the right child. The structure declaration for a binary
tree is as given below - File name: header.h

/* ****************************************************** ******************************************
Project : Binary Search Tree Implementation
Module : header.h [ Header file ]
Description : Library layer. Includes std headers structure decl,type defand manifests
Author : Cranes Software International,Bangalore.
Creation date:
* ********************************************************************************************** */
#include <stdio.h>
#include <stdlib.h>
struct tree
{
int key; /* The unique value of a node */
struct tree *left; /* self referential pointer to left sub
tree */
struct tree *right; /* self referential pointer to right
sub tree*/
};
typedef struct tree NODE;
NODE *root;

/* pointer for root node. Since it has been used by all the functions so it is global*/

Insertion into a Binary Search Tree

To insert a node in a BST, we must check whether the tree already contains any nodes. If tree is empty,
the node is placed as the root node. If the tree is not empty, the new node has to be inserted at such a
position that even after insertion; the properties of a BST are preserved.

Refer fig below,

© Cranes Varsity V2.1-2017-18 96/ 106


Advanced C & Data Structure www.cranesvarsity.com

To insert the new node in a non-empty tree, the following steps have to be performed.
 Set ptr to root.
 Compare newnode’s info with ptr’s info.
 If new’s info is greater than ptr’s info, traverse to the right subtree
prev=ptr;
ptr=ptr->right;
else (newnode’s info is less than ptr’s info), traverse to the left subtree -
prev=ptr;
ptr=ptr->left;
 If ptr is equal to NULL, then that node is free for insertion, go to step 5, else go to step 2.
 If newnode’s info is less than prev’s info, Make prev’s left link point to newnode else make prev’s
right point to newnode.

File name: modification.c

/* ************************************************************************************************ *
Project : Binary Search Tree Implementation
Module : Modification
Description : Implementation layer. Includes functions needed for the modification of BST.
Author : Cranes Software Intl, Bangalore.
Creation date: 11-08-2006
/************************************************************************************************* */

/* ****************************************************** ******************************************
Function Name : insert
Purpose : To insert a key to BST and hence to modify the tree.
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. reason)
************************************************************************************************* */
#include "header.h"
void insert(int val)
{
NODE *temp, *NewNode;
temp = root;
NewNode = (NODE)Create_Node ( );
NewNode->key = val;
if(root == NULL)
{
root = NewNode;
return ;
}

© Cranes Varsity V2.1-2017-18 97/ 106


Advanced C & Data Structure www.cranesvarsity.com

for(;;)
{
if(NewNode->key == temp->key)
{
fprintf(stderr,"Duplication not allowded\n");
free(NewNode);
break;
}
if (NewNode->key < temp->key)
{
if (temp->left == NULL)
{
temp->left = NewNode;
break;
}
else
temp = temp->left;
}
else
{
if (temp->right == NULL)
{
temp->right= NewNode;
break;
}
else
temp = temp->right;
}
}
}

Traversing a Binary Tree

The process of visiting or processing each node in the tree exactly one is called tree traversal. The
different traversal techniques are Inorder, Preorder and Post order. The name of the traversal method
depends on when the root is visited.
Each traversal technique can be expressed recursively.

The preorder traversal of a binary tree can be recursively defined as follows


 Process the root node.
 Traverse the left sub tree in preorder
 Traverse the right sub tree in preorder

The Inorder traversal of a binary tree can be recursively defined as follows


 Traverse the left sub tree in Inorder
 Process the root node.
 Traverse the right sub tree in Inorder

© Cranes Varsity V2.1-2017-18 98/ 106


Advanced C & Data Structure www.cranesvarsity.com

The Postorder traversal of a binary tree can be recursively defined as follows


 Traverse the left sub tree in Postorder
 Traverse the right sub tree in Postorder
 Process the root node.

For the given BST, the traversing using different traversal technique is

Preorder Inorder Postorder


20, 7, 4, 16, 38, 37, 43 4, 7, 16, 20, 37, 38, 43 4,16,7,37,43,38,20

Traversing a BST in inorder gives the elements in sorted order. The code for traversing and display in a
tree in three different ways is given below.

File name: display.c


/* ************************************************************************************************ *
Project : Binary Search Tree Implementation
Module : Display module
Description : Implementation layer. Includes functions needed for the traversal and
display. Basically preorder, inorder and post order traversal of BST.
Author : Cranes Software International,Bangalore.
Creation date:11-08-2006
* *********************************************************************************************** */
/* ****************************************************** *****************************************
Function Name : preorder
Purpose : Traversing recursively using preorder(Root,Left,Right) way and display each node.
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. reason)
* ****************************************************** ******************************************/
#include "header.h"
void preorder(NODE cnode)
{
if(cnode !=NULL)
{
printf("%d\t",cnode->key);
preorder(cnode->left);
preorder(cnode->right);
}
}
/* ********************************************************************************************** *
Function Name : inorder
Purpose : Traversing recursively using inorder(Left,Root,Right) way and display each node.
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. reason)
© Cranes Varsity V2.1-2017-18 99/ 106
Advanced C & Data Structure www.cranesvarsity.com

* *********************************************************************************************** */
void inorder(NODE cnode)
{
if(cnode != NULL)
{
inorder(cnode->left);
printf("%d\t",cnode->key);
inorder(cnode->right);
}
}
/* ****************************************************** ******************************************
Function Name : postorder
Purpose : Traversing recursively using postorder(Left,Right,Root) way and display each node.
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. reason)
**** ****************************************************** ***************************************/
void postorder(NODE cnode)
{
if(cnode !=NULL)
{
postorder(cnode->left);
postorder(cnode->right);
printf("%d\t",cnode->key);
}
}

Deletion of a node

Once again the node to be deleted is searched in BST. If found, we need to consider the following
possibilities:

 Deleting a leaf node: It can be deleted after making its parent’s edge pointing to the deleting node
set as null. The deleted node is now unreferenced and may be disposed off.
 If the node has one child; the deleting node’s parent's edge pointing to the deleting node has to
point to the deleting node’s child. For example for node 1 to be deleted from BST given in Figure
the left pointer of node 6 is made to point to child of node 1 i.e. node 4 and the new structure
would be

© Cranes Varsity V2.1-2017-18 100/ 106


Advanced C & Data Structure www.cranesvarsity.com

Before Deletion After deleting 1

 To delete a node that has two nonempty subtrees: the value is replaced by the smallest value in
the right sub tree or the largest key value in the left sub tree; subsequently the empty node is
recursively deleted. Consider the BST in Fig a. Suppose we wish to delete the element with key
40 from the tree.
Either the largest element from its left subtree (35) or the smallest element from its right subtree (60) can
replace the key element. If we opt for the smallest element in the right subtree, we move the element with
value 60 to the node from which 40 is deleted, and the leaf from which 60 is moved is deleted. The
resulting tree is shown is fig b.

Suppose, we opt for the largest element in the left subtree of 40. This element has value 35. We make
the left link of 40 is made to point to left link of 35. move the element 35 into the node that currently
contains 40.

Deleting Node with value 40 by replacing it with the


smallest element in the right subtree of 40.

© Cranes Varsity V2.1-2017-18 101/ 106


Advanced C & Data Structure www.cranesvarsity.com

Deleting Node with value 40 by replacing it with the


largest element in the left subtree of 40.

File name: modification.c

/* ****************************************************** *****************************************
Function Name : deletion
Purpose : To delete a node with the key value and to maintain the tree structure.
Parameters : (describe each argument, if req.)
return value : (explain if there is any specf. reason)
**** **********************************************************************************************/
/* Delete function */
void deletion( )
{
NODE parnt,parntsparnt;
NODE temp, ptemp;
NODE child;
int data;

printf("Enter the Node to be Deleted : ");


scanf ("%d", &data);

parnt = root;
parntsparnt = NULL;

while (parnt != NULL && parnt->key != data)


{
parntsparnt = parnt;
if (data < parnt->key)
parnt = parnt->left;
else
parnt = parnt->right;

© Cranes Varsity V2.1-2017-18 102/ 106


Advanced C & Data Structure www.cranesvarsity.com

}
if(parnt == NULL)
{
fprintf(stderr, "Node Not Found :\n");
return ;
}
printf("Deleting Node(%d) : \n", parnt->key);

if (parnt->left && parnt->right)//Two Children for the node


{
temp = parnt->left; // Get immediate left child
ptemp = parnt;

while (temp->right) // Go to the right most child


{
ptemp = temp;
temp = temp->right;
}
parnt->key = temp->key;
parnt = temp;
parntsparnt = ptemp;
}
if(parnt->left)
child = parnt->left;
else
child = parnt->right;
if (parnt == root)
root = child;
else
{
if(parnt == parntsparnt->left)
parntsparnt->left = child;
else
parntsparnt->right = child;
}
free (parnt);
parnt=NULL;
}

Expression tree

We can use binary trees to represent expression consisting of operators and operands. The expression
tree for an expression A + B is given below.

© Cranes Varsity V2.1-2017-18 103/ 106


Advanced C & Data Structure www.cranesvarsity.com

The operator is placed at the root. The left and right subtrees are the left and right operands of that
operator.

Traversing an expression tree in inorder gives the infix expression. Similarly traversing in preorder and
postorder gives the prefix and suffix expression respectively. The leaf nodes contain operands and non-
leaf nodes contains operator.

Expression tree for the expression A + (B – C) * D + E * F

Preorder : + + A * - B C D * E F

Postorder : A B C – D * + E F * +

Creating binary tree for the given postfix expression

Scan the symbols from left to right one at a time and create a node for each symbol. If the symbol is an
operand push the corresponding node to the stack. If the symbol is an operator, pop two nodes from the
stack, Attach the first popped node to the right and the second popped node to the left of the node
corresponding to the operator. Push the node corresponding to the operator to the stack.

Repeat this process for each symbol in the postfix expression. Finally when scanning of all the symbols in
the postfix expression is over, the top of the stack contains the root node of the expression tree.

Function to create the tree for the given postfix expression

struct node* create_tree(char postfix[ ])

© Cranes Varsity V2.1-2017-18 104/ 106


Advanced C & Data Structure www.cranesvarsity.com

{
struct node *st [20]; /* Stack to hold the nodes */
int top = -1;
int i;
char symbol;
struct node *temp;
for(i = 0;i < strlen (postfix) ; i++)
{
symbol = postfix[i];
temp = (struct node *) malloc (sizeof(struct node));
temp->info = symbol;
temp->llink = temp->rlink = NULL;
if(isdigit(symbol))
st[++top] = temp;
else
{
temp->rlink = st [top--];
temp->llink = st [top--];
st[++top] = temp;
}
}
return st [ top ];
}

Evaluation of expression represented using tree.


To evaluate an expression tree as an operator is encountered, evaluate the expression in the left subtree
and evaluate the expression in the right subtree and perform the operation.

The recursive definition to evaluate the expression is


Eval(root->llink) op Eval(root->rlink);
Eval(root) =
root->info

© Cranes Varsity V2.1-2017-18 105/ 106


Advanced C & Data Structure www.cranesvarsity.com

Bibilography:

 C in a Nutshell by Peter Prinz & Tony Crawford.


 The C Programming Language (ANSI C Version) by Dennis Ritchie & Brian Kernighan.
 Absolute C++ BY Walter Savitch & Kenrick Mock.
 C in Depth by S.K.Srivastava & Deepali Srivastava.

© Cranes Varsity V2.1-2017-18 106/ 106

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy