Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
13 views

C Programming AllClasses-Outline-198-233

The document discusses pointers and memory management in C programming. It explains dynamic memory allocation using malloc and free functions. It also discusses reallocating memory blocks using the realloc function. The document emphasizes the importance of pointer safety and ensuring memory pointed to by pointers is valid.

Uploaded by

SrinivasaRao
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

C Programming AllClasses-Outline-198-233

The document discusses pointers and memory management in C programming. It explains dynamic memory allocation using malloc and free functions. It also discusses reallocating memory blocks using the realloc function. The document emphasizes the importance of pointer safety and ensuring memory pointed to by pointers is valid.

Uploaded by

SrinivasaRao
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

Programming in C Unit 1

memory, use it for a while, but then reach a point where they don't need that
particular piece any more. Because memory is not inexhaustible, it's a good
idea to deallocate (that is, release or free) memory you're no longer using.
Dynamically allocated memory is deallocated with the free function. If p
contains a pointer previously returned by malloc, you can call
free(p);
which will “give the memory back'' to the stock of memory (sometimes called
the “arena'' or “pool'') from which malloc requests are satisfied. Calling free is
sort of the ultimate in recycling: it costs you almost nothing, and the memory
you give back is immediately usable by other parts of your program.
(Theoretically, it may even be usable by other programs.)
(Freeing unused memory is a good idea, but it's not mandatory. When your
program exits, any memory which it has allocated but not freed should be
automatically released. If your computer were to somehow “lose'' memory just
because your program forgot to free it, that would indicate a problem or
deficiency in your operating system.)
Naturally, once you've freed some memory you must remember not to use it
any more. After calling
free(p);
it is probably the case that p still points at the same memory. However, since
we've given it back, it's now “available,'' and a later call to malloc might give
that memory to some other part of your program. If the variable p is a global
variable or will otherwise stick around for a while, one good way to record the
fact that it's not to be used any more would be to set it to a null pointer:
free(p);
p = NULL;
Now we don't even have the pointer to the freed memory any more, and (as
long as we check to see that p is non-NULL before using it), we won't misuse
any memory via the pointer p.
When thinking about malloc, free, and dynamically-allocated memory in
general, remember again the distinction between a pointer and what it points
to. If you call malloc to allocate some memory, and store the pointer which
malloc gives you in a local pointer variable, what happens when the function
containing the local pointer variable returns? If the local pointer variable has
Manipal University Jaipur Page No.: 198
Programming in C Unit 1

automatic duration (which is the default, unless the variable is declared static),
it will disappear when the function returns. But for the pointer variable to
disappear says nothing about the memory pointed to! That memory still exists
and, as far as malloc and free are concerned, is still allocated. The only thing
that has disappeared is the pointer variable you had which pointed at the
allocated memory. (Furthermore, if it contained the only copy of the pointer you
had, once it disappears, you'll have no way of freeing the memory, and no way
of using it, either. Using memory and freeing memory both require that you
have at least one pointer to the memory!)
12.2.4 Reallocating Memory Blocks
Sometimes you're not sure at first how much memory you'll need. For example,
if you need to store a series of items you read from the user, and if the only
way to know how many there are is to read them until the user types some
“end'' signal, you'll have no way of knowing, as you begin reading and storing
the first few, how many you'll have seen by the time you do see that “end''
marker. You might want to allocate room for, say, 100 items, and if the user
enters a 101st item before entering the “end'' marker, you might wish for a way
to say ”uh, malloc, remember those 100 items I asked for? Could I change my
mind and have 200 instead?''
In fact, you can do exactly this, with the realloc function. You hand realloc an
old pointer (such as you received from an initial call to malloc) and a new size,
and realloc does what it can to give you a chunk of memory big enough to hold
the new size. For example, if we wanted the ip variable from an earlier example
to point at 200 ints instead of 100, we could try calling
ip = realloc(ip, 200 * sizeof(int));
Since you always want each block of dynamically-allocated memory to be
contiguous (so that you can treat it as if it were an array), you and realloc have
to worry about the case where realloc can't make the old block of memory
bigger “in place,'' but rather has to relocate it elsewhere in order to find enough
contiguous space for the new requested size. realloc does this by returning a
new pointer. If realloc was able to make the old block of memory bigger, it
returns the same pointer. If realloc has to go elsewhere to get enough
contiguous memory, it returns a pointer to the new memory, after copying your
old data there. (In this case, after it makes the copy, it frees the old block.)
Finally, if realloc can't find enough memory to satisfy the new request at all, it

Manipal University Jaipur Page No.: 199


Programming in C Unit 1

returns a null pointer. Therefore, you usually don't want to overwrite your old
pointer with realloc's return value until you've tested it to make sure it's not a
null pointer. You might use code like this:
int *newp;
newp = realloc(ip, 200 * sizeof(int));
if(newp != NULL)
ip = newp;
else {
printf("out of memory\n");
/* exit or return */
/* but ip still points at 100 ints */ }
If realloc returns something other than a null pointer, it succeeded, and we set
ip to what it returned. (We've either set ip to what it used to be or to a new
pointer, but in either case, it points to where our data is now.) If realloc returns
a null pointer, however, we hang on to our old pointer in ip which still points at
our original 100 values.
Putting this all together, here is a piece of code which reads lines of text from
the user, treats each line as an integer by calling atoi, and stores each integer
in a dynamically-allocated “array'':
#define MAXLINE 100
char line[MAXLINE];
int *ip;
int nalloc, nitems;

Manipal University Jaipur Page No.: 200


Programming in C Unit 1

nalloc = 100;
ip = (int *)malloc(nalloc * sizeof(int)); /* initial allocation */
if(ip == NULL)
{
printf("out of memory\n");
exit(1);
}

nitems = 0;

while(getline(line, MAXLINE) != EOF) {


if(nitems >= nalloc)
{ /* increase allocation */
int *newp;
nalloc += 100;
newp = realloc(ip, nalloc * sizeof(int)); if(newp == NULL)
{
printf("out of memory\n");
exit(1);
} ip =
newp;
}

ip[nitems++] = atoi(line);
}
We use two different variables to keep track of the “array'' pointed to by ip.
nalloc is how many elements we've allocated, and nitems is how many of them
are in use. Whenever we're about to store another item in the “array,'' if nitems
>= nalloc, the old “array'' is full, and it's time to call realloc to make it bigger.
Finally, we might ask what the return type of malloc and realloc is, if they are
able to return pointers to char or pointers to int or (though we haven't seen it
yet) pointers to any other type. The answer is that both of these functions are
declared (in <stdlib.h>) as returning a type we haven't seen, void * (that
15, pointer to void). We haven't really seen type void, either, but what's going
on here is that void * is specially defined as a “generic'' pointer type, which may
be used (strictly speaking, assigned to or from) any pointer type.

Manipal University Jaipur Page No.: 201


Programming in C Unit 1

Self Assessment Questions


1. The process of allocating memory at run time is known as
.
2. malloc() function returns a pointer to integer. (True/False)
3. For deallocating memory, you can use ________ function.
4. The function that is used to alter the size of a block previously allocated
is ________________ .

12.3 Pointer Safety


The hard thing about pointers is not so much manipulating them as ensuring
that the memory they point to is valid. When a pointer doesn't point where you
think it does, if you inadvertently access or modify the memory it points to, you
can damage other parts of your program, or (in some cases) other programs
or the operating system itself!
When we use pointers to simple variables, there's not much that can go wrong.
When we use pointers into arrays, and begin moving the pointers around, we
have to be more careful, to ensure that the moving pointers always stay within
the bounds of the array(s). When we begin passing pointers to functions, and
especially when we begin returning them from functions, we have to be more
careful still, because the code using the pointer may be far removed from the
code which owns or allocated the memory.
One particular problem concerns functions that return pointers. Where is the
memory to which the returned pointer points? Is it still around by the time the
function returns? The strstr function (as described in the earlier unit), returns
either a null pointer (which points definitively nowhere, and which the caller
presumably checks for) or it returns a pointer which points into the input string,
which the caller supplied, which is pretty safe. One thing a function must not
do, however, is return a pointer to one of its own, local, automatic arrays.
Remember that automatic variables (which includes all non-static local
variables), including automatic arrays, are deallocated and disappear when the
function returns. If a function returns a pointer to a local array, that pointer will
be invalid by the time the caller tries to use it.
Finally, when we're doing dynamic memory allocation with malloc, calloc,
realloc, and free, we have to be most careful of all. Dynamic allocation gives
us a lot more flexibility in how our programs use memory, although with that
Manipal University Jaipur Page No.: 202
Programming in C Unit 1

flexibility comes the responsibility that we manage dynamically allocated


memory carefully. The possibilities for misdirected pointers and associated
havoc are greatest in programs that make heavy use of dynamic memory
allocation. You can reduce these possibilities by designing your program in
such a way that it's easy to ensure that pointers are used correctly and that
memory is always allocated and deallocated correctly. (If, on the other hand,
your program is designed in such a way that meeting these guarantees is a
tedious nuisance, sooner or later you'll forget or neglect to, and maintenance
will be a nightmare.)

12.4 The Concept of Linked List


When dealing with many problems we need a dynamic list, dynamic in the
sense that the size requirement need not be known at compile time. Thus, the
list may grow or shrink during runtime. A linked list is a data structure that is
used to model such a dynamic list of data items, so the study of the linked lists
as one of the data structures is important.
An array is represented in memory using sequential mapping, which has the
property that elements are fixed distance apart. But this has the following
disadvantage: It makes insertion or deletion at any arbitrary position in an array
a costly operation, because this involves the movement of some of the existing
elements.
When we want to represent several lists by using arrays of varying size, either
we have to represent each list using a separate array of maximum size or we
have to represent each of the lists using one single array. The first one will lead
to wastage of storage, and the second will involve a lot of data movement.
So we have to use an alternative representation to overcome these
disadvantages. One alternative is a linked representation. In a linked
representation, it is not necessary that the elements be at a fixed distance
apart. Instead, we can place elements anywhere in memory, but to make it a
part of the same list, an element is required to be linked with a previous element
of the list. This can be done by storing the address of the next element in the
previous element itself. This requires that every element be capable of holding
the data as well as the address of the next element. Thus every element must
be a structure with a minimum of two fields, one for holding the data value,
which we call a data field, and the other for holding the address of the next
element, which we call link field.
Manipal University Jaipur Page No.: 203
Programming in C Unit 1

Therefore, a linked list is a list of elements in which the elements of the list can
be placed anywhere in memory, and these elements are linked with each other
using an explicit link field, that is, by storing the address of the next element in
the link field of the previous element.
Program 12.2: Here is a program for building and printing the elements
of the linked list
# include <stdio.h>
# include <stdlib.h> struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
/* if the existing list is empty then insert a new node as the
starting node */
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node)); /* creates new node data
value passes as parameter */
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = p; /* makes the pointer pointing to itself because it
is a circular list*/
}
else
{
temp = p;
/* traverses the existing list to get the pointer to the last node of
it */
while (temp-> link != p)
temp = temp-> link;

Manipal University Jaipur Page No.: 204


Programming in C Unit 1

temp-> link = (struct node *)malloc(sizeof(struct node)); /*


creates new node using data value passes as parameter and puts its address
in the link field of last node of the existing list*/
if(temp -> link == NULL)
{
printf("Error\n");
exit(0);
}
temp = temp-> link;
temp-> data = n;
temp-> link = p;
}
return (p);
}
void printlist ( struct node *p )
{
struct node *temp;
temp = p;
printf("The data values in the list are\n");
if(p!= NULL)
{
do
{
printf("%d\t",temp->data);
temp=temp->link;
} while (temp!= p);
}
else
printf("The list is empty\n");
}

void main()
{
int n;
int x;
struct node *start = NULL ;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
while ( n -- > 0 )
Manipal University Jaipur Page No.: 205
Programming in C Unit 1

{
printf( "Enter the data values to be placed in a node\n");
scanf("%d",&x);
start = insert ( start, x );
}
printf("The created list is\n");
printlist ( start );
}
12.4.1 Inserting a node by using Recursive Programs
A linked list is a recursive data structure. A recursive data structure is a data
structure that has the same form regardless of the size of the data. You can
easily write recursive programs for such data structures.
Program 12.3
# include <stdio.h>
# include <stdlib.h>
struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
p->link = insert(p->link,n);/* the while loop replaced by recursive call */
return (p);
}
Manipal University Jaipur Page No.: 206
Programming in C Unit 1

void printlist ( struct node *p )


{
printf("The data values in the list are\n");
while (p!= NULL)
{
printf("%d\t",p-> data);
p = p-> link;
}
}
void main()
{
int n;
int x;
struct node *start = NULL ;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
while ( n- - 0 )
{
printf( "Enter the data values to be placed in a node\n");
scanf("%d",&x);
start = insert ( start, x );
}
printf("The created list is\n"); printlist ( start );
}
12.4.2 Sorting and reversing a linked list
To sort a linked list, first we traverse the list searching for the node with a
minimum data value. Then we remove that node and append it to another list
which is initially empty. We repeat this process with the remaining list until the
list becomes empty, and at the end, we return a pointer to the beginning of
the list to which all the nodes are moved.
To reverse a list, we maintain a pointer each to the previous and the next
node, then we make the link field of the current node point to the previous,
make the previous equal to the current, and the current equal to the next.
Therefore, the code needed to reverse the list is
Prev = NULL;
While (curr != NULL)
Manipal University Jaipur Page No.: 207
Programming in C Unit 1

{
Next = curr->link;
Curr -> link = prev;
Prev = curr;
Curr = next;
}
Program 12.4: Example in sorting lists.
# include <stdio.h>
# include <stdlib.h> struct node {
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
{
temp = p;
while (temp-> link!= NULL)
temp = temp-> link;
temp-> link = (struct node *)malloc(sizeof(struct node));
if(temp -> link == NULL)
{
printf("Error\n");
exit(0);
}
temp = temp-> link;
Manipal University Jaipur Page No.: 208
Programming in C Unit 1

temp-> data = n;
temp-> link = null;
}
return(p);
}

void printlist ( struct node *p )


{
printf("The data values in the list are\n");
while (p!= NULL)
{
printf("%d\t",p-> data);
p = p-> link;
}
}

/* a function to sort reverse list */ struct node *reverse(struct node *p)


{
struct node *prev, *curr;
prev = NULL;
curr = p;
while (curr != NULL)
{
p = p-> link;
curr-> link = prev;
prev = curr;
curr = p;
}
return(prev);
}
/* a function to sort a list */
struct node *sortlist(struct node *p)
{
struct node *temp1,*temp2,*min,*prev,*q;
q = NULL;
while(p != NULL)
{
prev = NULL;
min = temp1 = p;

Manipal University Jaipur Page No.: 209


Programming in C Unit 1

temp2 = p -> link;


while ( temp2 != NULL )
{
if(min -> data > temp2 -> data)
{
min = temp2;
prev = temp1;
}
tempi = temp2;
temp2 = temp2-> link;
}
if(prev == NULL)
p = min -> link;
else
prev -> link = min -> link;
min -> link = NULL;
if( q == NULL)
q = min; /* moves the node with lowest data value in the list pointed to by
p to the list pointed to by q as a first node*/
else
{
tempi = q;
/* traverses the list pointed to by q to get pointer to its last node */
while( tempi -> link != NULL)
tempi = tempi -> link;
tempi -> link = min; /* moves the node with lowest data value in the
list pointed to by p to the list pointed to by q at the end of list pointed by q*/ }
}
return (q);
}

void main()
{
int n;
int x;
struct node *start = NULL ;
Manipal University Jaipur Page No.: 210
Programming in C Unit 1

printf("Enter the nodes to be created \n");


scanf("%d",&n);
while ( n- > 0 )
{
printf( "Enter the data values to be placed in a
node\n");

scanf("%d",&x);
start = insert ( start,x);
}
printf("The created list is\n");
printlist ( start );
start = sortlist(start);
printf("The sorted list is\n");
printlist ( start );
start = reverse(start);
printf("The reversed list is\n"); printlist ( start );
}
12.4.3 Deleting the specified node in a singly linked list
To delete a node, first we determine the node number to be deleted (this is
based on the assumption that the nodes of the list are numbered serially from
1 to n). The list is then traversed to get a pointer to the node whose number is
given, as well as a pointer to a node that appears before the node to be deleted.
Then the link field of the node that appears before the node to be deleted is
made to point to the node that appears after the node to be deleted, and the
node to be deleted is freed.
Program 12.5: Example of working with nodes.
# include <stdio.h>
# include <stdlib.h>
struct node *delet ( struct node *, int );
int length ( struct node * );
struct node
{
int data;
struct node *link;
};
struct node *insert(struct node *p, int n)
Manipal University Jaipur Page No.: 211
Programming in C Unit 1

{
struct node *temp;
if(p==NULL)
{
p=(struct node *)malloc(sizeof(struct node));
if(p==NULL)
{
printf("Error\n");
exit(0);
}
p-> data = n;
p-> link = NULL;
}
else
{
temp = p;
while (temp-> link != NULL)
temp = temp-> link;
temp-> link = (struct node *)malloc(sizeof(struct node));
if(temp -> link == NULL)
{
printf("Error\n");
exit(0);
}
temp = temp-> link;
temp-> data = n;
temp-> link = NULL;
}
return (p);
}
void printlist ( struct node *p )
{
printf("The data values in the list are\n");
while (p!= NULL)
{
printf("%d\t",p-> data);
p = p-> link;
}
Manipal University Jaipur Page No.: 212
Programming in C Unit 1

}
void main()
{
int n;
int x;
struct node *start = NULL;
printf("Enter the nodes to be created \n");
scanf("%d",&n);
while ( n-- > 0 )
{
printf( "Enter the data values to be placed in a node\n"); scanf("%d",&x);
start = insert ( start, x );
}
printf(" The list before deletion id\n");
printlist ( start );
printf("% \n Enter the node no \n");
scanf ( " %d",&n);
start = delet (start , n );
printf(" The list after deletion is\n");
printlist ( start );
}
/* a function to delete the specified node*/ struct node *delet ( struct node
*p, int node_no )
{
struct node *prev, *curr ;
int i;

if (p == NULL )
{
printf("There is no node to be deleted \n");
}
else
{
if ( node_no > length (p))
{
printf("Error\n");
}
Manipal University Jaipur Page No.: 213
Programming in C Unit 1

else
{
prev = NULL;
curr = p;
i=1;
while ( i < node_no )
{
prev = curr;
curr = curr-> link;
i = i+1;
}
if ( prev == NULL )
{
p = curr -> link; free ( curr );
} else {
prev -> link = curr -> link ; free ( curr );
}
}
} return(p);
}
/* a function to compute the length of a linked list */ int length ( struct node *p
)
{
int count = 0 ;
while ( p != NULL )
{
count++;
p = p->link;
}
return ( count ) ;
}
Self Assessment Questions
5. A linked list is a data structure that is used to model a dynamic list of data
items and is a collection of ______________ .
6. Linked list make use of ___________ memory allocation technique.

12.5 Summary
Manipal University Jaipur Page No.: 214
Programming in C Unit 1

The process of allocating memory at run time is known as Dynamic Memory


Allocation. The allocation of memory is done using three functions: malloc,
calloc, and realloc. These functions return the pointers to void, so it can be
typecast to any data type, thus making the functions generic. The memory
space that is used for dynamic allocation during execution of the program is
called heap. When a pointer doesn't point where you think it does, if you
inadvertently access or modify the memory it points to, you can damage other
parts of your program. So safety of pointers is essential in dynamic memory
allocation. Linked list is one of the applications of dynamic memory allocation.

12.6 Terminal Questions


1. Assuming that there is a structure definition with structure tag student,
write the malloc function to allocate space for the structure.
2. Write a structure definition to represent a node in the linked list with two
data fields of type integer.
3. If ptr points to a node and link is the address field of the node, how can
you move from the node to the next node?
4. Explain about the linked list.

12.7 Answers to Self Assessment Questions


1. Dynamic Memory Allocation.
2. False
3. free()
4. realloc()
5. nodes.
6. dynamic
12.8 Answers to Terminal Questions
1. ptr=(struct student *) malloc(sizeof(struct student));
2. struct node
{
int data1;
int data2;
struct node *link;
};

Manipal University Jaipur Page No.: 215


Programming in C Unit 1

3. ptr=ptr->link;
4. A linked list is a data structure that is used to model a dynamic list of data
items and is a collection of nodes. (Refer to section 12.4 for more details)

12.9 Exercises
1. Write a menu driven program to create a linked list of a class of students
and perform the following operations.
i. Write out the contents of a list
ii. Edit the details of a specified student
iii. Count the number of students above a specified age and weight
2. Write a program to insert a node after the specified node in alinkedlist.
3. Write a program to count the number of nodesin linked list.
4. Write a program to merge two sorted lists.
5. Explainbriefly how to represent polynomials using linked lists.Write a
program to add two polynomials.
Unit 13 File Management
Structure:
13.1 Introduction
Objectives
13.2 Defining and Openinga file
13.3 Closing Files
13.4 Input/Output Operationson Files
Predefined Streams
13.5 Error Handling during I/O Operations
13.6 Random Access to Files
13.7 Command Line Arguments
13.8 Summary
13.9 Terminal Questions
13.10 Answers to Self Assessment Questions
13.11 Answers to Terminal Questions
13.12 Exercises

13.1 Introduction
In nearly all the previous units, so far, we've been calling printf to print
formatted output to the “standard output'' (wherever that is). We've also been
Manipal University Jaipur Page No.: 216
Programming in C Unit 1

calling getchar to read single characters from the “standard input,'' and
putchar to write single characters to the standard output. “Standard input'' and
“standard output'' are two predefined I/O streams which are implicitly available
to us. In this unit, you will study how to take control of input and output by
opening our own streams, perhaps connected to data files, which we can read
from and write to.
Objectives:
After studying this unit, you should be able to:
• open your file and control input and output
• connect to file and able to read from the file and write to the file using file
pointers.
• checkg for error during I/O operations on files.
• implement the Random Access functions to allow control over the implied
read/write position in the file.
• supply a parameter to a program when the program is invoked using a
command line arguments.
13.2 Defining and Opening a File
How will we specify that we want to access a particular data file? It would
theoretically be possible to mention the name of a file each time it was desired
to read from or write to it. But such an approach would have a number of
drawbacks. Instead, the usual approach (and the one taken in C's stdio library)
is that you mention the name of the file once, at the time you open it.
Thereafter, you use some little token in this case, the file pointer which keeps
track (both for your sake and the library's) of which file you're talking about.
Whenever you want to read from or write to one of the files you're working with,
you identify that file by using its file pointer (that is, the file pointer you obtained
when you opened the file). As we'll see, you store file pointers in variables just
as you store any other data you manipulate, so it is possible to have several
files open, as long as you use distinct variables to store the file pointers.
You declare a variable to store a file pointer like this:
FILE *fp;
The type FILE is predefined for you by <stdio.h>. It is a data structure which
holds the information the standard I/O library needs to keep track of the file for
you. For historical reasons, you declare a variable which is a pointer to this
FILE type. The name of the variable can (as for any variable) be anything you

Manipal University Jaipur Page No.: 217


Programming in C Unit 1

choose; it is traditional to use the letters fp in the variable name (since we're
talking about a file pointer). If you were reading from two files at once you'd
probably use two file pointers:
FILE *fp1, *fp2;
If you were reading from one file and writing to another you might declare an
input file pointer and an output file pointer:
FILE *ifp, *ofp;
Like any pointer variable, a file pointer isn't good until it's initialized to point to
something. (Actually, no variable of any type is much good until you've
initialized it.) To actually open a file, and receive the “token'' which you'll store
in your file pointer variable, you call fopen. fopen accepts a file name (as a
string) and a mode value indicating among other things whether you intend to
read or write this file. (The mode variable is also a string.) To open the file
input.dat for reading you might call
ifp = fopen("input.dat", "r");
The mode string "r" indicates reading. Mode "w" indicates writing, so we could
open output.dat for output like this:
ofp = fopen("output.dat", "w");
The other values for the mode string are less frequently used. The third major
mode is "a" for append. (If you use "w" to write to a file which already exists,
its old contents will be discarded.) You may also add “+’’ character to the mode
string to indicate that you want to both read and write, or a “b’’ character to
indicate that you want to do “binary'' (as opposed to text) I/O.
One thing to beware of when opening files is that it's an operation which may
fail. The requested file might not exist, or it might be protected against reading
or writing. (These possibilities ought to be obvious, but it's easy to forget them.)
fopen returns a null pointer if it can't open the requested file, and it's important
to check for this case before going off and using fopen's return value as a file
pointer. Every call to fopen will typically be followed with a test, like this:
ifp = fopen("input.dat", "r");
if(ifp == NULL)
{
printf("can't open file\n"); exit or return
Manipal University Jaipur Page No.: 218
Programming in C Unit 1

}
If fopen returns a null pointer, and you store it in your file pointer variable and
go off and try to do I/O with it, your program will typically crash.
It's common to collapse the call to fopen and the assignment in with the test:
if((ifp = fopen("input.dat", "r")) == NULL)

{
printf("can't open file\n");
exit or return

}
You don't have to write these “collapsed'' tests if you're not comfortable with
them, but you'll see them in other people's code, so you should be able to read
them.
Self Assessment Questions
1. The type FILE is predefined in the header file __________ .
2. We may add a “+’’ character to the mode string in the fopen function to
indicate that we want to both read and write. (True/False)

13.3 Closing Files


Although you can open multiple files, there's a limit to how many you can have
open at once. If your program will open many files in succession, you'll want to
close each one as you're done with it; otherwise the standard I/O library could
run out of the resources it uses to keep track of open files. Closing a file simply
involves calling fclose with the file pointer as its argument:
fclose(fp);
Calling fclose arranges that (if the file was open for output) any last, buffered
output is finally written to the file, and that those resources used by the
operating system (and the C library) for this file are released. If you forget to
close a file, it will be closed automatically when the program exits.
Self Assessment Questions
3. Closing a file simply involves calling fclose with the ___________ as its
argument.
4. If you forget to close a file, it will be closed automatically when the program

Manipal University Jaipur Page No.: 219


Programming in C Unit 1

exits. (True/False)

13.4 Input/Output Operations on Files


For each of the I/O library functions we've been using so far, there's a
companion function which accepts an additional file pointer argument telling it
where to read from or write to. The companion function to printf is fprintf, and
the file pointer argument comes first. To print a string to the output.dat file we
opened in the previous section, we might call
fprintf(ofp, "Hello, world!\n");
The companion function to getchar is getc, and the file pointer is its only
argument. To read a character from the input.dat file we opened in the
previous section, we might call
int c;
c = getc(ifp);
The companion function to putchar is putc, and the file pointer argument
comes last. To write a character to output.dat, we could call
putc(c, ofp);
Our own getline function calls getchar and so always reads the standard
input. We could write a companion fgetline function which reads from an
arbitrary file pointer:
#include <stdio.h> /* Read one line from fp, */
/* copying it to line array (but no more than max chars). */
/* Does not place terminating \n in line array. */
/* Returns line length, or 0 for empty line, or EOF for end-of-file. */ int
fgetline(FILE *fp, char line[], int max)
{
int nch = 0;
int c;
max = max - 1; /* leave room for '\0' */ while((c =
getc(fp)) != EOF)
{
if(c == '\n') break;

if(nch < max) { line[nch] = c; nch = nch + 1; }

Manipal University Jaipur Page No.: 220


Programming in C Unit 1

}
if(c == EOF && nch == 0) return EOF;

line[nch] = '\0';
return nch;
}
Now we could read one line from ifp by calling char line[MAXLINE]; ...
fgetline(ifp, line, MAXLINE);

Program 13.1: Writing a data to the file


#include <stdio.h>
main( )
{
FILE *fp;
char stuff[25];
int index;
fp = fopen("TENLINES.TXT","w"); /* open for writing */ strcpy(stuff,"This is
an example line.");
for (index = 1; index <= 10; index++)
fprintf(fp,"%s Line number %d\n", stuff, index);
fclose(fp); /* close the file before ending program */
}
13.4.1 Predefined Streams
Besides the file pointers which we explicitly open by calling fopen, there are
also three predefined streams. stdin is a constant file pointer corresponding to
standard input, and stdout is a constant file pointer corresponding to standard
output. Both of these can be used anywhere a file pointer is called for; for
example, getchar() is the same as getc(stdin) and putchar(c) is the same as
putc(c, stdout). The third predefined stream is stderr. Like stdout, stderr is
typically connected to the screen by default. The difference is that stderr is not
redirected when the standard output is redirected. For example, under Unix or
MS-DOS, when you invoke
program > filename
anything printed to stdout is redirected to the file filename, but anything printed
to stderr still goes to the screen. The intention behind stderr is that it is the

Manipal University Jaipur Page No.: 221


Programming in C Unit 1

“standard error output''; error messages printed to it will not disappear into an
output file. For example, a more realistic way to print an error message when
a file can't be opened would be
if((ifp = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "can't open file %s\n", filename); exit or return
}
where filename is a string variable indicating the file name to be opened. Not
only is the error message printed to stderr, but it is also more informative in
that it mentions the name of the file that couldn't be opened.
Program 13.2: To read a data file input.dat
Suppose you had a data file consisting of rows and columns of numbers:
1 2 34
5 6 78
9 10 112
Suppose you wanted to read these numbers into an array. (Actually, the array
will be an array of arrays, or a “multidimensional'' array; ) We can write code to
do this by putting together several pieces: the fgetline function we just showed,
and the getwords function from which each line broken into words. Assuming
that the data file is named input.dat, the code would look like this:
#define MAXLINE 100
#define MAXROWS 10
#define MAXCOLS 10

int array[MAXROWS][MAXCOLS];
char *filename = "input.dat";
FILE *ifp;
char line[MAXLINE];
char *words[MAXCOLS];
int nrows = 0;
int n;
int i;

ifp = fopen(filename, "r");


if(ifp == NULL)
Manipal University Jaipur Page No.: 222
Programming in C Unit 1

{
fprintf(stderr, "can't open %s\n", filename);
exit(EXIT_FAILURE);
} while(fgetline(ifp, line, MAXLINE) != EOF) { if(nrows >=
MAXROWS) { fprintf(stderr, "too many rows\n"); exit(EXIT_FAILURE);
}

n = getwords(line, words, MAXCOLS);

for(i = 0; i < n; i++) array[nrows][i] = atoi(words[i]); nrows++;


}
Each trip through the loop reads one line from the file, using fgetline. Each line
is broken up into “words'' using getwords; each “word'' is actually one number.
The numbers are however still represented as strings, so each one is
converted to an int by calling atoi before being stored in the array. The code
checks for two different error conditions (failure to open the input file, and too
many lines in the input file) and if one of these conditions occurs, it prints an
error message, and exits. The exit function is a Standard library function which
terminates your program. It is declared in <stdlib.h>, and accepts one
argument, which will be the exit status of the program. EXIT_FAILURE is a
code, also defined by <stdlib.h>, which indicates that the program failed.
Success is indicated by a code of EXIT_SUCCESS, or simply 0. (These values
can also be returned from main(); calling exit with a particular status value is
essentially equivalent to returning that same status value from main.)
Self Assessment Questions
5. The companion function to putchar is putc, and the file pointer argument
comes first. (True/False)
6. Besides the file pointers which we explicitly open by calling fopen, there
are also ___________ predefined streams.
7. getchar() is the same as getc(stdin).(True/False)

13.5 Error Handling during I/O operations


The standard I/O functions maintain two indicators with each open stream to
show the end-of-file and error status of the stream. These can be interrogated
and set by the following functions:
#include <stdio.h>
void clearerr(FILE *stream);
Manipal University Jaipur Page No.: 223
Programming in C Unit 1

int feof(FILE *stream);


int ferror(FILE *stream);
void perror(const char *s);
clearerr clears the error and EOF indicators for the stream.
feof returns non-zero if the stream's EOF indicator is set, zero otherwise.
ferror returns non-zero if the stream's error indicator is set, zero otherwise.
perror prints a single-line error message on the program's standard output,
prefixed by the string pointed to by s, with a colon and a space appended. The
error message is determined by the value of errno and is intended to give some
explanation of the condition causing the error. For example, this program
produces the error message shown:
#include <stdio.h>
#include <stdlib.h>
main(){

fclose(stdout);
if(fgetc(stdout) >= 0){
fprintf(stderr, "What - no error!\n"); exit(EXIT_FAILURE);
}
perror("fgetc");
exit(EXIT_SUCCESS);
}

/* Result */
fgetc: Bad file number

Self Assessment Questions


8. clears the error and EOF indicators for the stream.
9. returns non-zero if the stream's error indicator is set, zero
otherwise.

13.6 Random Access to files


The file I/O routines all work in the same way; unless the user takes explicit
steps to change the file position indicator, files will be read and written
sequentially. A read followed by a write followed by a read (if the file was
opened in a mode to permit that) will cause the second read to start

Manipal University Jaipur Page No.: 224


Programming in C Unit 1

immediately following the end of the data just written. (Remember that stdio
insists on the user inserting a buffer-flushing operation between each element
of a read-write-read cycle.) To control this, the Random Access functions allow
control over the implied read/write position in the file. The file position indicator
is moved without the need for a read or a write, and indicates the byte to be
the subject of the next operation on the file.
Three types of function exist which allow the file position indicator to be
examined or changed. Their declarations and descriptions follow.
#include <stdio.h> /* return file position indicator */ long ftell(FILE *stream);
int fgetpos(FILE *stream, fpos_t *pos);

/* set file position indicator to zero */ void rewind(FILE *stream);

/* set file position indicator */ int fseek(FILE *stream, long offset, int ptrname);
int fsetpos(FILE *stream, const fpos_t *pos);
ftell returns the current value (measured in characters) of the file position
indicator if stream refers to a binary file. For a text file, a ‘magic’ number is
returned, which may only be used on a subsequent call to fseek to reposition
to the current file position indicator. On failure, -1L is returned and errno is set.
rewind sets the current file position indicator to the start of the file indicated by
stream. The file's error indicator is reset by a call of rewind. No value is
returned.
fseek allows the file position indicator for stream to be set to an arbitrary value
(for binary files), or for text files, only to a position obtained from ftell, as follows:
• In the general case, the file position indicator is set to offset bytes
(characters) from a point in the file determined by the value of ptrname.
Offset may be negative. The values of ptrname may be SEEK_SET, which
sets the file position indicator relative to the beginning of the file,
SEEK_CUR, which sets the file position indicator relative to its current
value, and SEEK_END, which sets the file position indicator relative to the
end of the file. The latter is not necessarily guaranteed to work properly on
binary streams.
• For text files, offset must either be zero or a value returned from a previous
call to ftell for the same stream, and the value of ptrname must be
SEEK_SET.

Manipal University Jaipur Page No.: 225


Programming in C Unit 1

• fseek clears the end of file indicator for the given stream and erases the
memory of any ungetc. It works for both input and output.
• Zero is returned for success, non-zero for a forbidden request.

Note that for ftell and fseek it must be possible to encode the value of the file
position indicator into a long. This may not work for very long files, so the
Standard introduces fgetpos and fsetpos which have been specified in a way
that removes the problem.
fgetpos stores the current file position indicator for stream in the object pointed
to by pos. The value stored is ‘magic’ and only used to return to the specified
position for the same stream using fsetpos.
fsetpos works as described above, also clearing the stream's end-of-file
indicator and forgetting the effects of any ungetc operations.
For both functions, on success, zero is returned; on failure, non-zero is
returned and errno is set.
Self Assessment Questions
10. function returns the current value (measured in
characters) of the file position indicator if stream refers to a binary file.
11. fseek allows the file position indicator for stream to be set to an arbitrary
value. (True/False)
12. stores the current file position indicator for stream in the
object pointed to by pos.

13.7 Command Line Arguments


We've mentioned several times that a program is rarely useful if it does exactly
the same thing every time you run it. Another way of giving a program some
variable input to work on is by invoking it with command line arguments. It is a
parameter supplied to a program when the program is invoked.
(We should probably admit that command line user interfaces are a bit old-
fashioned, and currently somewhat out of favor. If you've used Unix or MS-
DOS, you know what a command line is, but if your experience is confined to
the Macintosh or Microsoft Windows or some other Graphical User Interface,
you may never have seen a command line. In fact, if you're learning C on a
Mac or under Windows, it can be tricky to give your program a command line
at all.
Manipal University Jaipur Page No.: 226
Programming in C Unit 1

C's model of the command line is that it consists of a sequence of words,


typically separated by whitespace. Your main program can receive these
words as an array of strings, one word per string. In fact, the C run-time startup
code is always willing to pass you this array, and all you have to do to receive
it is to declare main as accepting two parameters, like this:
int main(int argc, char *argv[])
{
...
}
When main is called, argc will be a count of the number of command-line
arguments, and argv will be an array (“vector'') of the arguments themselves.
Since each word is a string which is represented as a pointer-to-char, argv is
an array-of-pointers-to-char. Since we are not defining the argv array, but
merely declaring a parameter which references an array somewhere else
(namely, in main's caller, the run-time startup code), we do not have to supply
an array dimension for argv. (Actually, since functions never receive arrays
as parameters in C, argv can also be thought of as a pointer-to- pointer-to-
char, or char **. But multidimensional arrays and pointers to pointers can be
confusing, and we haven't covered them, so we'll talk about argv as if it were
an array.) (Also, there's nothing magic about the names argc and argv. You
can give main's two parameters any names you like, as long as they have the
appropriate types. The names argc and argv are traditional.)
The first program to write when playing with argc and argv is one which
simply prints its arguments:

Program 13.3: To illustrate the use of command line arguments


#include <stdio.h>
main(int argc, char *argv[]) {
int i;

for(i = 0; i < argc; i++) printf("arg %d: %s\n", i, argv[i]);


return 0;
}
(This program is essentially the Unix or MS-DOS echo command.)
If you run this program, you'll discover that the set of “words'' making up the
Manipal University Jaipur Page No.: 227
Programming in C Unit 1

command line includes the command you typed to invoke your program (that
is, the name of your program). In other words, argv[0] typically points to the
name of your program, and argv[1] is the first argument.
There are no hard-and-fast rules for how a program should interpret its
command line. There is one set of conventions for Unix, another for MS- DOS,
another for VMS. Typically you'll loop over the arguments, perhaps treating
some as option flags and others as actual arguments (input files, etc.),
interpreting or acting on each one. Since each argument is a string, you'll have
to use strcmp or the like to match arguments against any patterns you might
be looking for. Remember that argc contains the number of words on the
command line, and that argv[0] is the command name, so if argc is 1, there are
no arguments to inspect. (You'll never want to look at argv[i], for i >= argc,
because it will be a null or invalid pointer.)
A further example of the use of argc and argv now follows:

Program 13.4: To illustrate the use of command line arguments


void main(int argc, char *argv[])
{
if (argc !=2) {
printf("Specify a password");
exit(1);
}
if (!strcmp(argv[1], "password")) printf("Access Permitted");
else
{
printf("Access denied");
exit(1);
} program code here
}
This program only allows access to its code if the correct password is entered
as a command-line argument. There are many uses for commandline
arguments and they can be a powerful tool.
As another example, here is a program which copies one or more input files to
its standard output. Since “standard output'' is usually the screen by default,
this is therefore a useful program for displaying files. (It's analogous to the

Manipal University Jaipur Page No.: 228


Programming in C Unit 1

obscurely-named Unix cat command, and to the MS-DOS type command.)


Program 13.5: To copy one or more input files to standard output
#include <stdio.h>
main(int argc, char *argv[])
{
int i;
FILE *fp;
int c;

for(i = 1; i < argc; i++)


{
fp = fopen(argv[i], "r");
if(fp == NULL)
{
fprintf(stderr, "cat: can't open %s\n", argv[i]);
continue;
}

while((c = getc(fp)) != EOF)


putchar(c);

fclose(fp); }

return 0;
}
As a historical note, the Unix cat program is so named because it can be used
to concatenate two files together, like this:
cat a b > c
This illustrates why it's a good idea to print error messages to stderr, so that
they don't get redirected. The “can't open file'' message in this example also
includes the name of the program as well as the name of the file.
Yet another piece of information which it's usually appropriate to include in
error messages is the reason why the operation failed, if known. For operating
system problems, such as inability to open a file, a code indicating the error is
often stored in the global variable errno. The standard library function strerror
will convert an errno value to a human-readable error message string.

Manipal University Jaipur Page No.: 229


Programming in C Unit 1

Therefore, an even more informative error message printout would be


fp = fopen(argv[i], "r");
if(fp == NULL) fprintf(stderr, "cat: can't open %s: %s\n", argv[i],
strerror(errno));
If you use code like this, you can #include <errno.h> to get the declaration for
errno, and <string.h> to get the declaration for strerror().
Final example program takes two command-line arguments. The first is the
name of a file, the second is a character. The program searches the specified
file, looking for the character. If the file contains at least one of these
characters, it reports this fact. This program uses argv to access the file
name and the character for which to search.
Program 13.6: To search the specified file, looking for the character
/*Search specified file for specified character. */
#include <stdio.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
FILE *fp; /* file pointer */ char ch;

/* see if correct number of command line arguments */


if(argc !=3) {
printf("Usage: find <filename> <ch>\n");
exit(1);
}

/* open file for input */


if ((fp = fopen(argv[1], "r"))==NULL) {
printf("Cannot open file \n");
exit(1);
}

/* look for character */


while ((ch = getc(fp)) !=EOF) /* where getc() is a */
if (ch== *argv[2]) { /function to get one char*/
printf("%c found",ch); /* from the file */ break;

Manipal University Jaipur Page No.: 230


Programming in C Unit 1

} fclose(fp);
}
Self Assessment Questions
13. Command line argument is a parameter supplied to a program when the
program is invoked. (True/False)
14. In the command line arguments of main(), argv is an array-of-pointers- to-
___________ .
15. The ____________ is a parameter supplied to a program when the
program is invoked.

13.8 Summary
“Standard input'' and “standard output'' are two predefined I/O streams which
are implicitly available to us. To actually open a file, and receive the “token''
which you'll store in your file pointer variable, you call fopen. fopen accepts a
file name (as a string) and a mode value indicating among other things whether
you intend to read or write this file. The file pointer which keeps track (both for
your sake and the library's) of which file you're talking about.
For each of the I/O library functions we've there's a companion function which
accepts an additional file pointer argument telling it where to read from or write
to. The standard I/O functions maintain two indicators with each open stream
to show the end-of-file and error status of the stream. The Random Access
functions allow control over the implied read/write position in the file. The
command line argument is a parameter supplied to a program when the
program is invoked.

13.9 Terminal Questions


1. The skeletal outline of a C program is shown below:
#include <stdio.h> main()
{
FILE *pt1, *pt2;
char name[20];

pt1 = fopen(“sample.old”, “r”);


pt2= fopen(“sample.new”,”w”);

fclose(pt1);

Manipal University Jaipur Page No.: 231


Programming in C Unit 1

fclose(pt2);
}
a) Read the string represented by name from the data file sample.old.
b) Display it on the screen
c) Enter the updated string.
d) Write the new string to the data file sample.new
2. What is a command line argument and what is its use? Explain

13.10 Answers to Self Assessment Questions


1. stdio.h
2. true
3. File pointer
4. True
5. false
6. three
7. True
8. clearerr
9. ferror
10. ftell
11. true
12. fgetpos
13. true
14. char
15. Command line argument
13.11 Answers to Terminal Questions
1. a) fscanf(pt1, “%s”, name);
b) printf(“Name: %s\n”, name);
c) puts(“New Name:”);
gets(name);
d) fputs(name, pt2);
2. Command Line argument is a parameter supplied to a program when the
program is invoked. (Refer 13.7 for more details)

13.12 Exercises

Manipal University Jaipur Page No.: 232


Programming in C Unit 1

1. Describe the use of functions getc() and putc().


2. What is meant by opening a data file? How is this accomplished?
3. Distinguish between the following functions:
a) getc and getchar
b) printf and fprintf
c) feof and ferror
4. Explain the general syntax of the fseek function.
5. Write a program to copy the contents of one file to another.
6. Two data files DATA1 and DATA2 contains sorted list of integers. Write a
program to produce a third file DATA which holds the single sorted ,
merged list of these two lists. Use command line arguments to specify the
file names.
7. The skeletal outline of an C program is shown below.
#include <stdio.h>
main()
{
FILE *pt1, *pt2;
int a;
float b;
char c;
pt1 = fopen(“sample.old”, “r”);
pt2= fopen (“sample.new”,”w”);
fclose(pt1);
fclose(pt2); }
a) Read the values of a,b and c from the data file sample.old.
b) Display each value on the screen and enter an updated value.
c) Write the new values to the data file sample.new. Format the floatingpoint
value so that not more than two decimals are written to sample.new.

Manipal University Jaipur Page No.: 233

You might also like