80 Intermediate C Programming
purpose : a condition that can never be true3
*/4
#in clude < stdio .h >5
#in clude < stdlib .h >6
int main ( i n t argc , char * argv [])7
{8
int x;9
int vx = 10;10
for (x = -100; x < 1000; x ++)11
{12
i f (( x < 0) && (x > 400) )13
{14
vx = -vx ;15
printf (" change d i rection n") ;16
}17
}18
return EXIT _SUCCES S ;19
}20
The tool gcov finds that the two lines
vx = -vx ;15
printf (" change d i rection n") ;16
are never executed. This tool works in collaboration with gcc, and additional arguments
are required when running gcc:
$ gcc -g -Wall -Wshadow -fprofile-arcs -ftest-coverage coverage.c -o cov
The executable file is called cov. Next, run the ./cov program:
$ ./cov
Two output files are generated: coverage.gcda and coverage.gcno. We can now run the
gcov command.
$ gcov coverage.c
The output is
File ’coverage.c’
Lines executed:71.43% of 7
coverage.c:creating ’coverage.c.gcov’
Another new file called coverage.c.gcov is generated. Here is the content of this file:
-: 0: Source : coverage .c1
-: 0: Graph : c overage . gcno2
-: 0: Data : coverage . gcda3
-: 0: Runs :14
-: 0: P r o grams :15
-: 1: /*6
-: 2: file : coverage .c7
-: 3: purpose : a conditi o n that can never be true8
-: 4: */9
Writing and Testing Programs 81
-: 5:# include < stdio .h >10
-: 6:# include < stdlib .h >11
1: 7: i nt main ( in t argc , char * argv [])12
-: 8:{13
-: 9: in t x ;14
1: 10: i n t vx = 10;15
1101: 11: for (x = -100; x < 1000; x ++)16
-: 12: {17
1100: 13: i f (( x < 0) && ( x > 400) )18
-: 14: {19
#####: 15: vx = -vx ;20
#####: 16: printf (" change d i rection n") ;21
-: 17: }22
-: 18: }23
1: 19: return E XIT_SUC CESS ;24
-: 20:}25
Lines 15 and 16 are marked by ##### because these two lines are never executed. This
tool can be used with complex programs to determine whether particular lines are never
executed. If typing these commands is too much work, then it is possible to write the
Makefile such that everything is handled by make every time the program is modified.
# Makefile f o r gcov1
GCC = gcc -g -Wall - Wshadow - fprofile - arcs - ftest - coverage2
cov : c o verage . c3
$( GCC ) coverage .c -o cov4
./ cov5
gcov c overage . c6
grep " #" covera g e .c . gcov7
clean :8
rm -f *. gcov *. gcno cov9
If we type make, this is the output:
gcc -g -Wall -Wshadow -fprofile-arcs -ftest-coverage coverage.c -o cov
./cov
gcov coverage.c
File ’coverage.c’
Lines executed:71.43% of 7
coverage.c:creating ’coverage.c.gcov’
grep "#" coverage.c.gcov
-: 5:#include <stdio.h>
-: 6:#include <stdlib.h>
#####: 15: vx = -vx;
#####: 16: printf("change direction ");
This Makefile also has an option called clean. Typing make clean deletes the file
generated by gcov. If gcov reports that some lines are never executed, then the problem
may come from the program, as shown in this case. Sometimes, the problem comes from
the test inputs. Designing good test inputs is not trivial and some books discuss in detail
how to design test inputs. Here are some suggestions.
Suppose you are writing a program searching whether a value is an element of a sorted
array. You should design test inputs to cover the following scenarios:
82 Intermediate C Programming
The value to search is an element of the array, somewhere in the middle of the array.
The value is not an element of the array but between some elements in the array.
The value is the same as the first element.
The value is the same as the last element.
The value is smaller than all elements.
The value is larger than all elements.
The array has only one element and the value is the same as this only element.
The array is empty and the value is irrelevant.
Why is it necessary to test these different cases? Depending on the search algorithm and
how the algorithm is implemented, one test case may fail to detect any problem. Creating
good test inputs is not trivial. A different approach is called formal verification by proving a
program is correct regardless of inputs. This is an advanced topic and will not be discussed
here.
It is important to understand the limitation of test coverage. Low coverage means that
the test inputs need improvement. However, high coverage is not necessarily better. A good
test input is one that can detect problems in your programs. A simple program like the
following can get 100% coverage:
#in clude < stdio .h >1
#in clude < stdlib .h >2
int main ( i n t argc , char * argv [])3
{4
return EXIT _SUCCES S ;5
}6
This program does not do anything. Pursuing high coverage should not be a goal in
itself. The goal should be detecting and fixing problems.
It is necessary to further explain the limitations of testing. Some students believe that
their programs are correct if the programs pass all test cases given by their professors. This is
wrong for a very simple reason: It is difficult, almost impossible, to test all possible scenarios.
Every if condition in a program creates two possible scenarios. Studies show that an if
condition appears approximately every 10 to 15 lines of code (excluding comments). If your
program has 15,000 lines, there are approximately 1,000 if conditions and 2
1000
possible
scenarios. How large is this number? The fastest computer in the world can perform about
50 × 10
15
(2
55
) operations per second. Testing 2
1000
scenarios would simply be impossible.
5.6 Limit Core Size
In some cases, invalid memory accesses will cause a “core dumped” message. The core
file is an old way to debug programs. Even though some people still use “core” to debug
their programs, this book does not teach how to do that. A core file can occupy a lot of
space on your disk. Use the following command to find whether a core file exists.
$ cd
$ find . -name ”core”
The first command returns to your home directory. The second command finds any core
file. Core files can be deleted by using the following command:
$ rm ‘find . -name ”core”‘
Writing and Testing Programs 83
The command rm means remove. The earlier command find . -name "core" is now
enclosed by single back-quotes. This is the quote mark sharing the key with . This is
not the single quote sharing the same key with the double quote ". The system settings
can be modified to eliminate cores. If you use the C shell, you can type
$ limit coredumpsize 0
Limiting the core size prevents the generation of a core file. This does not prevent
programs from making invalid memory accesses and having segmentation faults. We still
have to correct our programs and remove invalid memory accesses.
5.7 Programs with Infinite Loops
If a program has an infinite loop (i.e., a loop that will never end) and the program prints
something inside of the loop, then redirecting output to a file will create an infinitely large
file. Here is an example of an infinite loop:
#d ef in e MAX_VALU E 1001
int count = 0;2
while ( count < MAX_VAL U E )3
{4
printf (" some i nformati on n ") ;5
}6
This while loop will not end because count is zero and never changes. The program will
print forever. When this occurs, use Ctrl-c to stop the program. This means pressing the
Ctrl key (usually at the left lower corner of keyboard) and the c key at the same time. If
you suspect that your program generates exceptionally large files, you can find the existence
of these large files by using the following command in the Terminal:
$ cd
$ du -s * | sort -n
The first command returns to the home directory. The second line has two com-
mands: du -s * displays the space occupied by each directory. The output is then sorted
by treating the values as numbers (not strings). When sorting by numbers 10 comes after
9. When sorting by strings, 10 comes before 9. The vertical bar is called a pipe in Linux.
It takes the output from the first program and makes it the input to the second program.
By piping the directory sizes into sort we can easily see which directories occupy a lot of
space. We can the enter these directories and run the du command again to quickly find
large files.
This page intentionally left blankThis page intentionally left blank
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset