DS +apr 2021-2022+
DS +apr 2021-2022+
com
Prerequisite It is advised that the participants revise the topics of Programming in C Module.
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.
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.
#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
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’)
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.
#include <stdio.h>
void increment (int *x);
int main ( )
{
int count = 5;
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;
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
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.
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
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
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:
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
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).
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
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.
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.
/*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
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 .
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;
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;
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
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:
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>
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);
}
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.
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;
}
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]);
}
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);
}
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
#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
#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;
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.
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 */
}
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.
free ( ):
The syntax for the free ( ) function is
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.
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
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.
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
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:
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);
#include <stdio.h>
#include <stdlib.h>
int main( )
{
float *ptr1,*ptr2;
int i, n = 5;
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.
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).
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;
}
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.
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.
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.
#include<stdio.h>
int main()
int number,result;
scanf(“%d”,&number);
result = sum(number);
printf(“sum: %d”,result);
if(num!=0)
else
return num;
int main()
{
int i = 15;
printf("Factorial of %d is %d\n", i, factorial(i));
return 0;
}
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.
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.
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.
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’};
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;
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;
}
ptr = &s;
An individual structure element can be accessed using ptr using the -> operator
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.
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;
}
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;
}
display(st);
struct student s;
The structure is declared globally, so the function display ( ) can take an argument as student in the
program
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);
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.
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.
The structure date has been made as the member of structure student.
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
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.
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:
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
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 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.
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;
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 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
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*/
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;
BYTE x, y[10], * z;
size_t no_elements;
size_t class_strength;
void bubblesort(int arr[] , size_t n);
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
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.
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
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
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:
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;
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 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
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
The commonly used standard C-library functions for file handling can be categorized as follows
Opening Stream
SYNOPSIS
#include<stdio.h>
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.)
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.
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.
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
#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;
}
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:
SYNOPSIS
#include <stdio.h>
RETURN VALUES
Returns the character read as an unsigned char cast to an int or EOF on end of file or Error.
DESCRIPTION
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.
SYNOPSIS
#include<stdio.h>
RETURN VALUES
DESCRIPTION
Example
#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 */
}
the fgets function is an improvement over fscanf and scanf. It reads the entire line of data from a file
SYNOPSIS
#include<stdio.h>
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.
SYNOPSIS
#include<stdio.h>
RETURN VALUES:
DESCRIPTION
fputs ( ) writes a null terminated string s to the specified stream. Note that the null byte at the end is not
written.
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;
}
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
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
Prototype:
#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
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
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.
RETURN VALUES
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:
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>
RETURN VALUES
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
Similarly, to read the string from the file identified by ofp into the variable string, the statement is,
could be used.
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
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
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.
……………….
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.
#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);
}
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);
}
#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;
}
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.
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
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:
except that the error indicator for the stream is also cleared (see clearerr(3)).
#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));
#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);
}
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.
Stack Implementation
- Static arrays
- Array
Dynamic arrays
- Linked List
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
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
Application layer
Header file
/*************************************************************************************************
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);
/* *************************************************************************************************
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;
}
/* ****************************************************** *******************************************
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]);
}
/* *************************************************************************************************
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 *****/
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.
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 +, 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
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;
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.
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
/***************************************************************************************************
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 */
/* ****************************************************** *******************************************
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;
/* *************************************************************************************************
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
****************************************************************************************************
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
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
int len;
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)
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.
Queue Implementation:
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
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.
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.
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.
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
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;
};
/*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
/* ****************************************************** ******************************************
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;
/* *********************************************************************************************** *
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;
}
/* *********************************************************************************************** *
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;
}
/* ****************************************************** ******************************************
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
*/
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;
if(temp == NULL)
return L_NOT_FOUND;
else
temp->data = newData;
return L_SUCCESS;
}
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
/* ************************************************************************************************ *
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.
/******************************************* ****************************************************** *
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);
}
return L_SUCCESS;
}
/* ************************************************************************************************ *
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
* *********************************************************************************************** */
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;
delNode = delNode->nxt;
} while (delNode != NULL && delNode->info != key);
if(delNode == NULL)
return L_NOT_FOUND;
free(delNode);
return L_SUCCESS;
}
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.
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.
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
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.
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;
};
The operations that can be performed on doubly linked lists are similar to those on singly linked lists.
They are –
/*function declarations*/
NODE * Create_Node(int element) ;
/*end of Dlist.h*/
/* ************************************************************************************************ *
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;
}
/* *************************************************************************************************
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;
if(front != NULL)
{
newnnode ->nxt = front;
front->prev = newnnode;
}
front = newnode;
return L_SUCCESS;
}
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;
}
/* *********************************************************************************************** *
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;
}
/* ********************************************************************************************** *
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 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…
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 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.
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*/
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.
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.
/* ************************************************************************************************ *
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 ;
}
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;
}
}
}
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.
For the given BST, the traversing using different traversal technique is
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.
* *********************************************************************************************** */
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
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.
/* ****************************************************** *****************************************
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;
parnt = root;
parntsparnt = NULL;
}
if(parnt == NULL)
{
fprintf(stderr, "Node Not Found :\n");
return ;
}
printf("Deleting Node(%d) : \n", parnt->key);
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.
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.
Preorder : + + A * - B C D * E F
Postorder : A B C – D * + E F * +
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.
{
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 ];
}
Bibilography: