Lab #11
Lab #11
Objectives
1. Structures
2. Unions
3. Enumerators
4. Linked List and its operations
5. File Handling
Instead of defining a structure we can define independent variables for first name, last name, and
age. However creating and managing variables to manipulate the information of 100 or 1000 uses
is both confusing and tedious.
Note that the above code only defines a structure person. However, it does not create
any variable of type person. We can declare a structure variable as follows:
11.2 Union
Like structure, union is a user defined data type. In union, all members share the same
memory location (i.e. Unions only allocate enough space to store the largest member
variable, and all fields are stored at the same space.). Thus, only one variable can be
used at any instant of time. For example in the following C program, both x and y share
the same location. If we change x, we can see the changes being reflected in y.
#include <stdio.h>
typedef union test { int x , y ; } T;
int main ()
{
T t ;
t.x = 2 ;
printf(”After making x=2: \n x=%, y=%d\n\n”, t.x, t.y);
t.y = 10 ;
printf(”After making Y=10 : \ n x=%d, y=%d\n\n”, t.x, t.y) ;
return 0 ;
}
Unions are useful when the type of data being passed through functions is unknown,
using a union which contains all possible data types can remedy this problem.
The usage of unions are similar to that of structures. Hence, all the things discussed
above for structures are valid for unions too.
11.3 Enum
Enumeration (or enum) is a user defined data type in C. It is mainly used to assign names
to integral constants, the names make a program easy to read and maintain.
Unnamed type:
enum {ZERO, ONE};
Values may be assigned to specific enum value names. Any names without assigned values will
get one higher than the previous entry. If the first name does not have an assigned value, it
gets the value of zero. It is even legal to assign the same value to more than one name.
enum Errors {
11.4.3 Representation in C
A linked list is represented by a pointer to the first node of the linked list. The first node is called head. If
the linked list is empty, then value of head is NULL. Each node in a list consists of at least two parts:
(a) data
(b) pointer to the next node
In C, we can represent a node using structures. Below is an example of a linked list node with an integer
data.
struct node
{
int data;
struct node *next;
};
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
/* data has been assigned to data part of second block (block pointed by
second). And next pointer of the second block points to third block.
So all three blocks are linked.
/* data has been assigned to data part of third block (block pointed
by third). And next pointer of the third block is made NULL to indicate
that the linked list is terminated here.
head
|
|
+---+---+ +---+---+ +----+------+
| 1 | o----->| 2 | o-----> | 3 | NULL |
+---+---+ +---+---+ +----+------+
Note that only head is sufficient to represent the whole list. We can
traverse the complete list by following next pointers. */
return 0;
}
#include<stdio.h>
#include<stdlib.h>
struct node
{
int data;
struct node *next;
};
// This function prints contents of linked list starting from the given node
void printList(struct node *n)
{
while (n != NULL)
{
printf(" %d ", n->data);
n = n->next;
}
}
int main()
{
struct node* head = NULL;
struct node* second = NULL;
struct node* third = NULL;
printList(head);
return 0;
}
/* Given a node prev_node, insert a new node after the given prev_node */
void insertAfter(struct node* prev_node, int new_data)
{
/*1. check if the given prev_node is NULL */
if (prev_node == NULL)
{
printf("the given previous node cannot be NULL");
return;
}
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
int main()
{
/* Start with the empty list */
struct node* head = NULL;
return 0;
}
11.4.7 Deletion algorithm is one of the basic algorithm which is required to maintain the list.
Basically the deletion algorithm adjusts the pointer of the relevant nodes to make sure that the list
remains in the correct order (consistent) after the node is removed.
Consider the diagrammatic representation of the linked list given below which holds a character value:
The diagram above shows the final form of the linked list. The task is to remove a node form the linked
list with a given value X (which can be a parameter to the function delete).
ListDelete (&list, X); // address of the list and value of the node must be
passed.
We'll consider our elements and nodes to have the following types:
and thus, the pointer to the beginning of the list will be: nodeT *list;
Different cases:
There are a few steps to deleting a specific element from the list:
1. Find the node with the element (if it exists).
2. Remove that node.
3. Reconnect the linked list.
4. Update the link to the beginning (if necessary).
Finding the node in question is a matter of traversing the list and looking at each node's element.
Reconnecting the list once a node is to be removed is more interesting. Let's consider at least 3 cases:
• Removing a node from the beginning.
• Removing a node from the middle.
• Removing a node from the end.
When removing the node at the beginning of the list, there is no relinking of nodes to be performed,
since the first node has no preceding node. For example, removing node with a:
list
|
v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------
list
|
+-------------+
|
v
--------- --------- ---------
| a | --+---> | b | --+---> | c | 0 |
--------- --------- ---------
Removing a node from the middle requires that the preceding node skips over the node being removed.
For example, removing the node with b:
list
|
v
--------- --------- ---------
| a | --+--+ | b | --+---> | c | 0 |
--------- | --------- ---------
| ^
+----------------+
This means that we need some way to refer to the node before the one we want to remove.
Removing a node from the end requires that the preceding node becomes the new end of the list (i.e.,
points to nothing after it). For example, removing the node with c:
list
|
v
--------- --------- ---------
| a | --+---> | b | 0 | | c | 0 |
--------- --------- ---------
Note that the last two cases (middle and end) can be combined by saying that "the node preceding the
one to be removed must point where the one to be removed does."
In addition, since we need to fix the pointer to the beginning of the list, we'll need some way to get the
new beginning pointer out of the function. One way to do so (and the one we'll initially use) is to return
the pointer from the deletion function. This means we will have a deletion function as follows:
Implementation:
Again, we'll need to be able to change the pointer to the list, i.e., list, in the case that the first node in
the list is removed. However, instead of passing back a new value for the beginning of the list via the
return mechanism, as in:
we'll pass in the pointer to the beginning of the list by reference (as a pointer to a pointer), so that we
can change it in ListDelete() when necessary. Thus, the prototype for our function will be:
Note: This time, we pass the address of the "list" variable to the function, so it makes sense to call the
parameter that receives that address "listP", since it is a pointer to a "list".
Now we will iterate (i.e., loop) through the list. It's easy to see how to start a pointer at the beginning
and move it from one node to the next:
In other words:
• Start at the beginning:
• currP = *listP
Note that to access the address of the first node, we have to dereference listP with the star (*)
since listP is a "pointer to a pointer to the first node" (and we want to remove one level of
pointer-ness to get the "pointer to the first node").
• Advance to the next node when necessary:
• currP = currP->next
• Stop when there are no more nodes, i.e., make sure that:
• currP != NULL
However, when we find the one to remove, we'll also need a pointer to the previous node:
--------- --------- ---------
| x | --+---> | y | --+---> | z | --+--->
--------- --------- ---------
^ ^
| |
prevP currP
Thus, we also need to maintain a previous pointer at each step of the loop:
// Done searching
return;
}
}
}
We do handle the situation of removing the first node, i.e., when there is no previous. See how we use a
previous pointer value of NULL to indicate this and do the right thing.
A file represents a sequence of bytes on the disk where a group of related data is stored. File is
created for permanent storage of data. It is a readymade structure.
FILE *fp;
There are five major operations that can be performed on a file are:
➢ Creation of a new file.
➢ Opening an existing file.
➢ Reading data from a file.
➢ Writing data in a file.
➢ Closing a file.
is given below:
The fopen() function is used to create a new file or to open an existing file. The syntax of fopen()
function is given as:
FILE *fopen( const char * filename, const char * mode );
Here filename is the name of the file to be opened and mode specifies the purpose of opening the
file. The access mode can have one of the following values:
The fclose() function is used to close an already opened file. The prototype of this function is:-
int fclose( FILE *fp );
Here fclose() function closes the file and returns zero on success, or EOF if there is an error in
closing the file. This EOF is a constant defined in the header file stdio.h.
11.5.8 Writing a File
The fputc() function is used to write a single character into file. It outputs a character to a stream.
int fputc(int c, FILE *fp);
The function fputc() writes the character value of the argument c to the output stream referenced
by fp. It returns the written character written on success otherwise EOF if there is an error.
Given below is the simplest function to read a single character from a file –
The fgetc() function reads a character from the input file referenced by fp. The return value is the
character read, or in case of any error, it returns EOF. The following function allows to read a string
from a stream –
The functions fgets() reads up to n-1 characters from the input stream referenced by fp. It copies
the read string into the buffer buf, appending a null character to terminate the string.
Exercises:
1. Write a program to copy contents of one file to another file. While doing so replace all
lowercase characters to their equivalent uppercase characters.
2. Write a program to append the content of one file at the end of another.
3. Write a program to find the number of lines in a text file.