Program Testing and Debugging in C

Program testing and debugging refer to the tasks of detecting and removing errors in a program, so that the program produces the desired results on all occasions. Every programmer should be aware of the fact that rarely does a program run perfectly the first time. No matter how thoroughly the design is carried out, and no matter how much care is taken in coding, one can never say that the program would be 100 per cent error free. It is therefore necessary to make efforts to detect, isolate and correct any errors that are likely to be present in the program.

Types of Errors

We have discussed a number of common errors. There might be many other errors, some obvious and four types, namely, syntax errors, run time others not so obvious. All these errors can be classified under errors, logical errors, and latent errors.

Syntax errors

Any violation of rules of the language results in syntax errors. The compiler can detect and isolate such errors. When syntax errors are present, the compilation fails and is terminated after listing the errors and the line numbers in the source program, where the errors have occurred. Remember, in some cases, the line number may not exactly indicate the place of the error. In other cases, one syntax error may result in a long list of errors. Correction of one or two errors at the beginning of the program may eliminate the entire list.

Run time errors

Errors such as a mismatch of data types or referencing an out of range array element go undetected by the compiler. A program with these mistakes will run, but produce erroneous results and therefore, the name run time errors is given to such errors. Isolating a run time error is usually a difficult task.

Logical errors

As the name implies, these errors are related to the logic of the program execution. Such actions as taking a wrong path, failure to consider a particular condition, and incorrect order of evaluation of statements belong to this category. Logical errors do not show up as compiler generated error messages. Rather, they cause incorrect results. These errors are primarily due to a poor understanding of the problem, incorrect translation of the algorithm into the program and a lack of clarity of hierarchy of operators. Consider the following statement:

if(x ==y)
printf(“They are equal\n”);

when x and y are float types values, they rarely become equal, due to truncation errors. The printf call may not be executed at all. A test like while(x != y) might create an infinite loop.

Latent errors

It is a ‘hidden’ error that shows up only when a particular set of data is used. For example, consider the following statement: ratio = (x+y)/(p-q);

An error occurs only when p and q are equal. An error of this kind can be detected only by using all possible combinations of test data.

Program Testing

Testing is the process of reviewing and executing a program with the intent of detecting errors, which may belong to any of the four kinds discussed above. We know that while the compiler can detect syntactic and semantic errors, it cannot detect run time and logical errors that show up during the execution of the program. Program testing and debugging, therefore, should include necessary steps to detect all possible errors in the program. It is, however, important to remember that it is impractical to find all errors. Testing process may include the following two stages:

  1. Human testing.
  2. Computer-based testing.

Human testing is an effective error detection process and is done before the computer based testing begins. Human testing methods include code inspection by the programmer, code inspection by a test group, and a review by a peer group. The test is carried out statement by statement and is analyzed with respect to a checklist of common programming errors. In addition to finding the errors, the programming style and choice of algorithm are also reviewed.

Computer based testing involves two stages, namely compiler testing and run time testing. Compiler testing is the simplest of the two and detects yet undiscovered syntax errors. The program executes when the compiler detects no more errors. Should it mean that the program is correct? Will it produce the expected results? The answer is negative. The program may still contain run-time and logic errors.

Run time errors may produce run time error messages such as “null pointer assignment” and “stack overflow”. When the program is free from all such errors, it produces output ,which might or might not be correct. Now comes the crucial test, the test for the expected output. The goal is to ensure that the program produces expected results under all conditions of input data.

Test for correct output is done using test data with known results for the purpose of comparison. The most important consideration here is the design or invention of effective test data. A useful criteria for test data is that all the various conditions and paths that the processing may take during execution must be tested.

Program testing can be done either at module (function) level or at program level. Module level test, often known as unit test, is conducted on each of the modules to uncover errors within the boundary of the module. Unit testing becomes simple when a module is designed to perform only one function.

Once all modules are unit tested, they should be integrated together to perform the desired function(s). There are likely to be interfacing problems, such as data  mismatch between the modules. An integration test is performed to discover errors associated with interfacing.

Program Debugging

Program testing and debugging is the process of isolating and correcting the errors. One simple method of debugging is to place print statements throughout the program to display the values of variables. It displays the dynamics of a program and allows us to examine and compare the information at various points. Once the location of an error is identified and the error corrected, the debugging statements may be removed. We can use the conditional compilation statements, discussed in before, to switch on or off the program testing and debugging statements.

Another approach is to use the process of deduction. The location of an error is arrived at using the process of elimination and refinement. This is done using a list of possible causes of the error.

The third error locating method is to backtrack the incorrect results through the logic of the program until the mistake is located. That is, beginning at the place where the symptom has been uncovered, the program is traced backward until the error is located.

Two critical resources of a computer system are execution time and memory. The efficiency of a program is measured in terms of these two resources. Efficiency can be improved with good design and coding practices.

Execution Time

The execution time is directly tied to the efficiency of the algorithm selected. However, certain coding techniques can considerably improve the execution efficiency. The following are some of the techniques, which could be applied while coding the program.

  • Select the fastest algorithm possible.
  • Simplify arithmetic and logical expressions.
  • Use fast arithmetic operations, whenever possible.
  • Carefully evaluate loops to avoid any unnecessary calculations within the loops.
  • If possible, avoid the use of multi-dimensional arrays.
  • Use pointers for handling arrays and strings.

However, remember the following, while attempting to improve efficiency.

  • Analyse the algorithm and various parts of the program before attempting any efficiency changes.
  • Make it work before making it faster.
  • Keep it right while trying to make it faster.
  • Do not sacrifice clarity for efficiency.

Memory Requirement

Memory restrictions in the micro computer environment is a real concern to the programmer. It is therefore, desirable to take all necessary steps to compress memory requirements for program testing and debugging.

  1. Keep the program simple. This is the key to memory efficiency.
  2. Use an algorithm that is simple and requires less steps.
  3. Declare arrays and strings with correct sizes.
  4. When possible, limit the use of multi-dimensional arrays.
  5. Try to evaluate and incorporate memory compression features available with the language.
Read More Topics
The TCP/IP reference model
Five layers in reference model
Artificial intelligence and robotics
Goals of artificial intelligence (AI)

About the author

Santhakumar Raja

Hi, This blog is dedicated to students to stay update in the education industry. Motivates students to become better readers and writers.

View all posts

Leave a Reply