Unit V - Debugging and File Handling
Unit V - Debugging and File Handling
STRUCTURE
5.1 Introduction
5.2.1 Debugging
5.4 Summary
5.5 Keywords
5.8 References
169
5.1 INTRODUCTION
Testing an application against a well-chosen set of enter checks offers the programmer
confidence that the program is correct. throughout the checking out procedure, the
programmer observes enter-output relationships, this is, the output that this system produces
for every enter test case. If this system produces the anticipated output and obeys the
specification for every check case, then this system is efficaciously examined.
however, if the output for one of the check cases is not the one anticipated, then this system is
wrong -- it consists of errors (or defects, or "insects"). In such situations, trying out handiest
exhibits the presence of errors, but does not tell us what the errors are, or how the code needs
to be fixed. In other phrases, testing famous the results (or signs and symptoms) of mistakes,
not the reason of mistakes. The programmer ought to then go through a debugging procedure,
to identify the causes and connect the mistakes.
Exceptionally, the debugging manner may take considerably greater time than writing the
code within the first vicinity. A big amount (if not most) of the development of a bit of
software program goes into debugging and preserving the code, in place of writing it.
consequently, the fine factor to do is to avoid the trojan horse when you write the program
within the first location! it's far vital to sit down and think before you code: determine exactly
what desires to be finished, how you intend to perform that, design the excessive-stage
algorithm cleanly, persuade yourself it's miles correct, determine what are the concrete
information structures you propose to use, and what are the invariants you plan to maintain.
all the effort spent in designing and considering the code before you write it will pay off later.
The benefits are twofold. First, having a clean design will reduce the opportunity of defects to
your application. 2nd, even though a worm shows up all through trying out, an easy layout
with clear invariants will make it tons less complicated to track down and fasten the worm.
it may be very tempting to write down the program as fast as viable, leaving little or no time
to think about it earlier than. The programmer can be satisfied to peer the program done in a
short quantity. however, it is likely he'll get pissed off quickly afterwards: without
170
appropriate thinking, the program might be complex and uncertain, so preservation and
computer virus solving will become an infinite method.
once the programmer starts off evolved coding, he needs to use defensive programming. that
is just like shielding driving, because of this riding below worst-case eventualities (e.g.,
different drivers violating traffic laws, unexpected activities or obstacles, etc.). in addition,
defensive programming method developing code such that it really works efficiently
underneath the worst-case scenarios from its environment. as an instance, while writing a
function, one must expect worst-case inputs to that function, i.e., inputs that are too large, too
small, or inputs that violate a few properties, condition, or invariant; the code should address
these instances, even supposing the programmer does not count on them to occur underneath
normal instances.
recall, the intention isn't to grow to be an expert at solving bugs, however as an alternative to
get higher at writing sturdy, (mainly) mistakes-free applications in the first location. As a be
counted of attitude, programmers should no longer sense proud after they restore insects,
however alternatively embarrassed that their code had bugs. If there is a computer virus
within the software, it's miles simplest due to the fact the programmer made mistakes.
5.2.1 Debugging
Although there is no precise procedure for fixing all bugs, there are a number of useful
strategies that can reduce the debugging effort. A significant part (if not all) of this process is
spent localizing the error, that is, figuring out the cause from its symptoms. Below are several
useful strategies to help with this. Keep in mind that different techniques are better suited in
different cases; there is no clear best method. It is good to have knowledge and experience
with all of these approaches. Sometimes, a combination of one or more of these approaches
will lead you to the error.
Incremental and bottom-up program development. One of the most effective ways to localize
errors is to develop the program incrementally, and test it often, after adding each piece of
code. It is highly likely that if there is an error, it occurs in the last piece of code that you
wrote. With incremental program development, the last portion of code is small; the search
for bugs is therefore limited to small code fragments. An added benefit is that small code
increments will likely lead to few errors, so the programmer is not overwhelmed with long
lists of errors.
171
Bottom-up development maximizes the benefits of incremental development. With bottom-up
development, once a piece of code has been successfully tested, its behavior won't change
when more code is incrementally added later. Existing code doesn't rely on the new parts
being added, so if an error occurs, it must be in the newly added code (unless the old parts
weren't tested well enough).
Instrument program to log information. Typically, print statements are inserted. Although the
printed information is effective in some cases, it can also become difficult to inspect when the
volume of logged information becomes huge. In those cases, automated scripts may be
needed to sift through the data and report the relevant parts in a more compact format.
Visualization tools can also help understanding the printed data. For instance, to debug a
program that manipulates graphs, it may be useful to use a graph visualization tool (such as
ATT's graph viz) and print information in the appropriate format (.dot files for graph viz).
Instrument program with assertions. Assertions check if the program indeed maintains the
properties or invariants that your code relies on. Because the program stops as soon as it an
assertion fails, it's likely that the point where the program stops is much closer to the cause,
and is a good indicator of what the problem is. An example of assertion checking is the
repOK() function that verifies if the representation invariant holds at function boundaries.
Note that checking invariants or conditions is the basis of defensive programming. The
difference is that the number of checks is usually increased during debugging for those parts
of the program that are suspected to contain errors.
Use debuggers. If a debugger is available, it can replace the manual instrumentation using
print statements or assertions. Setting breakpoints in the program, stepping into and over
functions, watching program expressions, and inspecting the memory contents at selected
points during the execution will give all the needed run-time information without generating
large, hard-to-read log files.
Backtracking. One option is to start from the point where to problem occurred and go back
through the code to see how that might have happened.
Binary search. The backtracking approach will fail if the error is far from the symptom. A
better approach is to explore the code using a divide-and-conquer approach, to quickly pin
down the bug. For example, starting from a large piece of code, place a check halfway
through the code. If the error doesn't show up at that point, it means the bug occurs in the
172
second half; otherwise, it is in the first half. Thus, the code that needs inspection has been
reduced to half. Repeating the process a few times will quickly lead to the actual problem.
Problem simplification. A similar approach is to gradually eliminate portions of the code that
are not relevant to the bug. For instance, if a function fun f() = (g();h();k()) yields an error, try
eliminating the calls to g, h, and k successively (by commenting them out), to determine
which is the erroneous one. Then simplify the code in the body of buggy function, and so on.
Continuing this process, the code gets simpler and simpler. The bug will eventually become
evident. A similar technique can be applied to simplify data rather than code. If the size of the
input data is too large, repeatedly cut parts of it and check if the bug is still present. When the
data set is small enough, the cause may be easier to understand.
A scientific method: form hypotheses. A related approach is as follows: inspect the test case
results; form a hypothesis that is consistent with the observed data; and then design and run a
simple test to refute the hypothesis. If the hypothesis has been refuted, derive another
hypothesis and continue the process. In some sense, this is also a simplification process: it
reduces the number of possible hypotheses at each step. But unlike the above simplification
techniques, which are mostly mechanical, this process is driven by active thinking about an
explanation. A good approach is to try to come with the simplest hypotheses and the simplest
corresponding test cases.
Bug clustering. If a large number of errors are being reported, it is useful to group them into
classes of related bugs (or similar bugs), and examine only one bug from each class. The
intuition is that bugs from each class have the same cause (or a similar cause). Therefore,
fixing a bug with automatically fix all the other bugs from the same class (or will make it
obvious how to fix them).
Error-detection tools. Such tools can help programmers quickly identify violations of certain
classes of errors. For instance, tools that check safety properties can verify that file accesses
173
in a program obey the open-read/write-close file sequence; that the code correctly
manipulates locks; or that the program always accesses valid memory. Such tools are either
dynamic (they instrument the program to find errors at run-time), or use static analysis (look
for errors at compile-time). For instance, purify is a popular dynamic tool that instruments
programs to identify memory errors, such as invalid accesses or memory leaks. Examples of
static tools include ESC Java and Spec#, which use theorem proving approaches to check
more general user specifications (pre- and post-conditions, or invariants); or tools from a
recent company Coverity that use dataflow analysis to detect violations of safety properties.
Such tools can dramatically increase productivity, but checking is restricted to a particular
domain or class of properties. There is also an associated learning curve, although that is
usually low. Currently, there are relatively few such tools and this is more an (active) area of
research.
Software testing refers to the process of executing an application or program with the intent
of detecting potential software bugs. It also has the objective of helping the developers to find
out whether the software is working according to the intended purpose. During the testing
process, the end user’s area of application will be considered and the software will be
subjected to a series of tests to ensure it satisfies the needs of the end users.
This will include several testings for several factors that may affect the performance of the
system such as the presence of a bug, programming error, and other hidden dangers to the
software. When these shortcomings are detected during the testing process, they will be
corrected without delay before the completion of the software system.
There are different types of system testing techniques. These are static testing, dynamic
testing, unit testing, integration testing, and other testing techniques. All these testing
techniques have a single function: to ensure that the software does not fail to meet the
expectations of the end users, the people who the program is designed for.
The testing can either be done manually by the programmers. This will require that they will
go through the program to hand pick the errors for correction. The automated can also be
used for testing but it functions by using some tools and scripts. Although the techniques are
different and have their advantages,
174
On the other hand, we have software verification, another important tool they each constitute
a great way of identifying the errors in a program. There are contributory factors to the high
quality of a software program. What is it?
Before a system is developed, there must be a design where the basic requirements for the
system are well spelt out. During the development of the system, verification is done to
consider whether the system is developed according to the requirements and specifications.
This may include all the activities that are combined to make sure that high-quality software
is released for use to the end users.
Inspection
Specification analysis
Testing
Design analysis
The verification process is usually at the beginning of a development process and all the
processes above will be done for the evaluation of some important plan, code, and
specifications. The objective of verification is to answer the question “Are we developing the
software according to the specification and requirements?”
For instance, banking software will be expected to be used for processing customer
information, balance the account in case of withdrawal or otherwise, and other related
functions. If verification is to be performed on the software, the testers will test whether these
functions can be performed effectively by the software. If a banking software can update a
withdrawal but cannot do the same for saving, it will generate a lot of problems. If
verification is performed, such problem will be easily corrected. If any of the features of the
software malfunctions, the defect will render the performance of the system useless.
While there is a stark difference between system testing and system verification, one of the
system verification methods is system testing. It is testing the software that will reveal
whether it is developed in conformity with the laid down design and purpose.
175
5.3 FILE MANAGEMENT
The data was written to the standard output and data was read from the standard input. As
long as only small amount of data is being accessed in the form of simple variables, and
character strings this type of I/O is sufficient. However, with large amounts of data. The
information has to be written to or read from auxiliary storage device. Such information
stored on the device in the form of a data file.
The second category of stream-oriented data files, often referred to as unformatted data files,
organizes data into blocks containing contiguous bytes of information. These blocks represent
more complex data structures, such as arrays and structures. These files are called binary
files.
System oriented data files are more closely related to the computer’s operating system than
are stream oriented data files. To perform the I/O from and to files, an extensive set of library
functions are available in Access to files generally requires four basic operations.
Open: This allows access to a file and establishes the position, offset, in the file.
Close: This ends access to the file. When access to a file completes, it should be closed. The
number of files that a running program can have any time is limited; by closing file properly
these limited facilities can be used more intelligently.
Read: This gets information from the file, either in the form of characters strings, or in the
form of data (combined integers, characters, floating point numbers, and structures).
Write: This adds information to the file or replaces information already in the file
176
Operations on Files
1. Opening a file
2. Reading from a file
3. Writing a file
4. Appending to a file
5. Updating a file
6. Deleting a file
7. Renaming a file
8. Closing a file
These operations are accomplished by means of standard library functions that are provided
by C.
fopen ()
The fopen () is to open a file. Opening a file basically establishes a link between a program
and the file being opened.
SYNTAX
fp=fopen(“filename‟,”mode of opening”);
where,
filename->is the name of the file being opened(which is remembered by the operating
system).Mode of opening can be any one of the following.
w To create a text file. If the file already exits, its contents are destroyed; otherwise, it is
created, if possible.
177
exist.
a To open a text file for appending (writing at the end); if the file does not exist, it is
created, if possible.
if the file already exists, its contents are destroyed; otherwise, it is created, if possible.
a+ To open a text file for both reading and appending; if the file already exists, its
contents are retained; if the file does not
wb To create a file. If the file already exists, its contentsare destroyed; otherwise,
it is
created, if possible.
must exist
ab To open a binary file for both reading and writing; if the file already exists, its
contents are destroyed; otherwise, it is created, if
possible.
wb+ To create a binary file both reading and writing; if the file already exists, its contents
are destroyed; otherwise, it is created, if
possible.
ab+ To open a binary file for reading and appending; if the file already exists, its
contents are retained; if the file does not exist, it is created if possible.
178
5.3.2 closing a file
fclose ()
The fclose () is the counterpart of fopen ().This is used to close a file. Closing a file name
means de-linking the file from the program and saving the contents of the file.
SYNTAX
fclose (fp);
where
fp->is the pointer to FILE type and represents a file. The file represented by fp is closed.
When a file opened, we can read data stored in the file or write new data onto it depending on
the mode of opening standard library supports a good number of functions which can be used
for performing I/O operations. These functions are referred to as file I/O functions.
high level file I/O functions are basically C standard library functions and are easy to use.
Most of the C programs handling files use these because of their simple nature. Low level file
I/O functions are file related system calls of the underlying operating system. These are
relatively more complex in nature when compared to high level file I/O functions but
efficient in nature.
High level file I/O functions can be further classified into the following two types:
179
1. Unformatted file I/O functions
SYNTAX
fputc(c,fp);
where
c-> represents a character and fp,a pointer to FILE, represents a file. The function writes the
content of c onto the file represented by fp.
fgetc() is to read a character from a file. The syntax of its usage is as follows:
c=fgetc (fp);
180
c is a variable of char type and fp is a pointer to FILE type. The function reads a character
from the file denoted by fp and returns the character value, which is collected by the variable
c.
Example
#include<stdio.h> #include<conio.h>
void main()
fp=fopen(“text”,”w”);
c=getchar();
while(c!=‟q‟)
fputc(c,fp); c=getchar();
fclose(fp);
181
String Oriented Functions-fputs(),fgets()
Syntax
fputs (buffer,fp);
buffer is the name of a character array, size is an integer value, fp is a pointer to FILE type.
The function reads a string of maximum size-1 characters from the file pointed to by fp and
copies it to the memory area denoted by buffer.
Example
#include<stdio.h> #include<conio.h>
void main()
FILE *fp;
char name[20];
clrscr();
fp=fopen(“name.dat”,”r”);
while(!feof(fp))
fgets(name,80,fp);
puts(name); printf(“\n”);
fclose(fp);
182
Strings are
Ravichandran
fprintf() is to write multiple data items which may or may not be of different types to a file.
SYNTAX
fprintf() is similar to that of prinf() except the presence of an extra parameter fp,a pointer to
FILE type. The parameter fp represents the file to which data are to be written.
fscanf() is similar to that of scanf() except the presence of an extra parameter fp. A pointer to
FILE type. The parameter fp represents the file from which data are to be read.
Example
float salary;
};
void main()
FILE *fp;
183
struct emp e;
fp=fopen(emp.dat”,”r”);
while(!feof(fp))
fscanf(fp,”%d %s %f”,&e.empno,&e.name,&e.salary);
fclose(fp);
The structure struct emp is defined with the fields empno, name and salary. In the main ()
variable fp is declared to be a pointer to FILE type and it is to denote the file emp.dat to be
created by the program.e is declared to be a variable of struct emp type, which is to collect
the employee details accepted through the keyboard.
The fwrite () is used to write blocks of data (records) to a file. The contents which are written
to the secondary devices are nothing but the exact copy of them in memory. The files of this
kind are called binary files. But the fprintf(),another file output function, formats the
memory contents according to the format specifies passed to it and then write them onto the
secondary storage device.
fwrite(buffer_address,size,count,file_pointer);
here,buffer_address is the address of the memory area, the contents of which are to be written
to the file denoted by the fourth arguments file_pointer.The second argument size specifies
the number of bytes of a block(record) and count specifies the number of blocks of data
written to the file.
184
Example
};
void main()
struct emp e;
FILE *p;
fp=fopen(“emp.data”,”wb”);
scanf(“%d”,&n);
for(i=1;i<=n;i++)
scanf(“%d %s %f”,&e.empno,&e.name,&e.salary);
fwrite(&e,sizeof(e),1,fp);
fclose(fp); getch();
185
120 kirupa 83832
Reading and writing a character from/to a file fgetc() is used for reading a character from
the file
Syntax:
Syntax:
fputc(character,file_pointer);
void main()
If(fs=fopen(“scr.txt”,”r”)==0)
If(fd=fopen(“dest.txt”,”w”)==0)
186
printf(“Sorry….The destination file cannot be opened”); fclose(fs);
return;
while(ch=fgets(fs)!=EOF) fputc(ch,fd);
fcloseall();
Reading and writing a string from/to a file getw() is used for reading a string from the file
Syntax:
gets(file pointer);
Syntax:
void main()
187
/*extract the word*/ word=getw(fp); If(ferror(fp))
printf(“Successful read:word=%d\n”,word);
Reading and writing a string from/to a file fgets() is used for reading a string from the file
Syntax:
Syntax:
void main(void)
FILE*stream;
188
5.4 SUMMARY
5.5 KEYWORDS
FILE: The header file stdio.h defines a new data type called FILE.
STDIO.H: Each file used in a program must be associated with a pointer of its type.
Three types of file pointers are defined in stdio.h. They are stdin, stdout and stderr.
PRINTF () AND SCANF (): printf () and scanf) are used for performing input/output
operations in programs (without involving files).
FPRINTF () and FSCANF (): fprintf () and fscanf () are used to perform
input/output operation in files.
FGETC () and FPUTC (): Single character input/output from files are fetc() and
fputc(). FGETCHAR () and FPUTCHAR (): Character input/output functions
which act on files. END OF FILE:Most of the programs use EOF when reading input
from the user or when displaying the output data.
FEOF(): The feof() function is used to check the end of file condition
189
5.6 LEARNING ACTIVITY
___________________________________________________________________________
___________________________________________________________________________
___________________________________________________________________________
___________________________________________________________________________
A. Descriptive Questions
Short Questions
4. What are standard files that are accessed when a program begins its execution?
5. Explain the functions fopen() and fclose() and with clearly stating its syntax?
Long Questions
2. Explain the functions fetc() and fputc() with clearly stating its syntax?
190
B. Multiple Choice Questions
1. A mode which is used to open an existing file for both reading and writing ______
a. ”W”
b. ”W+”
c. ”R+”
d. ”A+”
a. pits()
b. putc()
c. fputs()
d. fgets()
3. Select a function which is used to read a single character from a file at a time?
a. fscanf()
b. getch()
c. fgetc()
d. fgets()
a. printf()
b. fprintf()
c. puts()
d. fputs()
191
5. Select a program which get input data from datafile and also send output into datafile ,it is
called _____
a. files
b. file processing
c. data files
d. file handling
Answer
5.8 REFERENCE
References Book
192