Errors

When developing a program that solves some practical problem, it’s almost inevitable that errors are added by the programmer to the code. Obviously, programmers strive after programs without errors.

Several tools and techniques can be used to help programmers to identify errors in the code and eliminate them. Two examples one can mention are debuggers and software testing techniques. A debugger is a tool that can execute a program step-by-step, stop (pause) at a particular instruction indicated by the programmer by means of a breakpoint, and trace the values of variables. Almost all IDEs, like Visual Studio or Xcode, come equipped with a debugger.

Let us look into different types of errors which programmers often talk about.

Compilation errors

Compilation errors are errors that the compiler can detect. These errors are usually the easiest to detect and correct. For instance, if one forgets the semi-colon in the end of a statement or to declare a variable before it is used then the compiler produces a compilation error.

For instance, consider a program that reads two positive integers, d1 and d2, and then displays d1 / d2.

1
2
3
4
5
6
7
8
9
#include <iostream>

int main() {
    std::cout << "Enter two positive numbers:\n";
    double d1;
    std::cin >> d1 >> d2;

    std::cout << d1 << "/" << d2 << "= " << d1 / d2 << "\n";
}

When compiling the program above, a compilation error is shown. A message similar to “use of undeclared identifier d2” is generated by the compiler beacuse variable d2 used in line $6$ has not been defined. Try to compile the program above!

Note that no object file is produced by the compiler, if compilation errors have been detected.

These errors are produced by the linker when trying to produce an executable by putting together the object file for your program with code in other object files, like library files. For instance, if two libraries used in a program define a function named sqrt to compute the square root of a number then the following line of code leads to a link error. The reason is that the linker is unable to decide which of the functions should be used.

std:cout << sqrt(66);

This concrete problem can be easily addressed by adding to sqrt the name of the library we mean to use. For example, std::cout << std::sqrt(66); states that function sqrt from the the standard library of C++ should be used.

Run-time errors

Run-time errors are detected automatically when the program is executing and, typically, the program ends abruptly with some incomprehensible message to most of the users. These type of errors cause often irritation in the users and are costly for companies. Consequently, software applications should be free from run-time errors.

For example, consider again the program that reads two positive integers, d1 and d2, and then displays d1 / d2. As you can see, the compilation error has been fixed (variable d2 is defined).

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main() {
    std::cout << "Enter two positive numbers:\n";
    double d1;
    double d2;
    std::cin >> d1 >> d2;

    std::cout << d1 << "/" << d2 << "= " << d1 / d2 << "\n";
}

The program above can lead to a run-time errors, if the user enters zero for d2. Just try by yourself the program above with the input 33 0. The problem here is division in line $9$ which can lead to a division by zero, if the user innocently enters zero for d2. Division by zero is not mathematically defined and this situation is detected by the computer’s hardware which terminates the program with some cryptic message, such as “22 / 0 = inf” (assuming the user enters the input integers 22 0).

The division by zero in the program above can be easily prevented by testing whether variable d2 stores a value different from zero before the code in line $9$ is executed, as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main() {
    std::cout << "Enter two positive integers:\n";
    double d1;
    double d2;
    std::cin >> d1 >> d2;

    if (d2 != 0) {
        std::cout << d1 << "/" << d2 << "= " << d1 / d2 << "\n";
    } else {
        std::cout << "Not possible to divide by zero!!\n";
    }
}

Note that the program above gives a sensible message to the user, if there is an attempt to divide by zero, instead of just relying in some cryptic message generated by the system when a run time error occurs.

Logic errors

Logic errors usually manifest themselves by the fact that the program produces incorrect results, for some inputs. These can be the most challenging type of errors to detect and fix. Often, they are the result of using an erroneous algorithm (strategy) to solve the problem.

The debugger can be a great aid tool for programmers to find logical errors in the code. With the debugger, it’s possible to follow the program execution step by step and inspect the values stored in the variables.