5. Errors
5.1 Introduction

While developing a program, errors are unacceptable mistakes done by programmer often referred to as bugs. To maintain the quality of a program, errors need be removed and a program should be free of errors to be considered acceptable.
The errors can be classified as follows:
Type of Error | Found By |
---|---|
Compile-time errors (Syntax, Type Errors) |
Compiler |
Link-time errors | Linker (During combination of object files into an exceutable program) |
Run-time errors | Checks (Computer/ Library/ User Code) |
Logic errors | Programmer (Looking for the causes of erroneous results) |
How to deal with errors to produce an acceptable software ?
Answer: Follow these 3 basic approaches
- Organize software to minimize errors
- Eliminate most of the errors we made through debugging and testing
- Make sure the remaining errors are not serious
5.2 Sources of errors
- Poor specification & Incomplete programs: Occurs when all cases are not handled including error cases.
- Unexpected arguments:
Occurs when a argument is not handled in a function.
Example:sqrt(-1.2)
Sincesqrt()
of adouble
returnsdouble
, there is no possible correct return value. - Unexpected input:
Occurs when input cases are not handled.
Example: When user enters a string input instead of a expected integer input - Unexpected state: Most programs keep a lot of data around for use by different parts of the system. What if such a data is wrong or incomplete?
- Logical errors: The code just doesn't do what you meant it to.
5.3 Compile-time errors

Compiler analyzes to detect syntax errors and type
errors.
Many of the errors are simply "silly" errors
caused due to incomplete edits of the code or mistyping.
5.3.1 Syntax errors
int s1 = area(7; // error: ) missing
int s2 = area(7) // error: ; missing
Int s3 = area(7); // error: Int is not a type
int s4 = area('7); // error: non-terminated character (' missing)
For example, given the error in the declaration of s3
above, a compiler is unlikely to say
“You misspelled int
; don’t capitalize the
i
.”
Rather, it’ll say something like
“syntax error: missing ‘;
’
before identifier ‘s3
’”
“‘s3
’ missing storage-class or
type identifiers”
“‘Int
’ missing
storage-class or type identifiers”
Drill
Write the code above in a file and see what errors are produced.
5.3.2 Type errors
After syntax errors, the compiler begins looking for type errors.
int x0 = arena(7); // error: undeclared function
int x1 = area(7); // error: wrong number of arguments
int x2 = area("seven",2); // error: 1st argument has a wrong type
5.3.3 Non-errors
int x4 = area(10,–7); // OK: but what is a rectangle with a width of minus 7?
For x4
, no error message from the
compiler. area()
asks for two integers
irrespective of whether it is positive or negative.
Therefore, area (10,–7)
is fine.
5.4 Link-time errors
Translational units - several separately compiled parts in a program.
int area(int length, int width); // calculate area of a rectangle
int main()
{
int x = area(2,3);
}
The linker will complain that it didn't find a definition
area()
, unless area()
is defined
in some other source file and it is linked to the
generated code from the source file.
Also, both the
return type and the argument types must be same as that
of the file and the definition of area()
.
5.5 Run-time errors

Even after the program is syntactically correct it may
exit unexpectedly during execution.
In such case it
encounters a runtime error.
We say, a program crashes
when it halts due to a runtime error.
Examples:
- division by zero
- accessing a non-existing file, dictionary value, object attribute or list element
- using a non-defined identifier
- operation on incompatible types
int area(int length, int width) // calculate area of a rectangle
{
return length*width;
}
int framed_area(int x, int y) // calculate area within frame
{
return area(x–2,y–2);
}
int main()
{
int x = –1;
int y = 2;
int z = 4;
// . .
int area1 = area(x,y);
int area2 = framed_area(1,z);
int area3 = framed_area(y,z);
double ratio = double(area1)/area3; // convert to double to get
// floating-point division
}
The above calls lead to negative values of areas,
being assigned to area1
and
area2
.
Also, look at the calculation of the ratio in the code
above. It looks innocent enough.
Did you notice something wrong with it?
Look again:
area3
will be 0
,
so that double(area1)/area3
divides by zero.
The above leads to a hardware-detected error
that terminates the program. This problem can be dealt in
two ways:
- The caller of
area()
deal with bad arguments. area()
(the called function) deal with bad arguments.
5.5.1 The caller deals with errors
Protect the call of area(x,y)
in
main()
:
if (x<=0) error("non-positive x");
if (y<=0) error("non-positive y");
int area1 = area(x,y);
To complete protecting area()
from bad arguments, we have to deal with the calls
through framed_area()
:
if (z<=2)
error("non-positive 2nd area() argument called by framed_area()");
int area2 = framed_area(1,z);
if (y<=2 || z<=2)
error("non-positive area() argument called by framed_area()");
int area3 = framed_area(y,z);
This is messy,
but there is also something fundamentally
wrong.
What if someone modified framed_area()
to use 1
instead of 2
?
Someone doing that would have to look at
every call of framed_area()
and modify
the error-checking code correspondingly.
Such code is called “brittle” because it breaks
easily.
We could make the code less brittle by giving the
value subtracted by framed_area()
a name:
constexpr int frame_width = 2;
int framed_area(int x, int y) // calculate area within frame
{
return area(x–frame_width,y–frame_width);
}
That name could be used by code calling
framed_area()
:
if (1–frame_width<=0 || z–frame_width<=0)
error("non-positive argument for area() called by framed_area()");
int area2 = framed_area(1,z);
if (y–frame_width<=0 || z–frame_width<=0)
error("non-positive argument for area() called by framed_area()");
int area3 = framed_area(y,z);
Look at that code!
Are you sure it is correct?
Do you find it pretty? Is it easy to read?
Actually, we find it ugly (and therefore
error-prone).
We have more than trebled the size of the code
and exposed an implementation detail of
framed_area()
. There
has to be a better way!
Look at the original code:
int area2 = framed_area(1,z);
int area3 = framed_area(y,z);
It may be wrong, but at least we can see what it is
supposed to do. We can keep this code if we
put the check inside framed_area()
.
5.5.2 The callee deals with errors
int framed_area(int x, int y) // calculate area within frame
{
constexpr int frame_width = 2;
if (x–frame_width<=0 || y–frame_width<=0)
error("non-positive area() argument called by framed_area()");
return area(x–frame_width,y–frame_width);
}
This is rather nice, and we no longer have to write a
test for each call of framed_area()
.
Furthermore, if anything to do with the error
handling changes, we only have to modify the code
in one place.
int area(int length, int width) // calculate area of a rectangle
{
if (length<=0 || width <=0) error("non-positive area() argument");
return length*width;
}
This will catch all errors in calls to
area()
, so we no longer need to check
in framed_area()
.
5.5.3 Error reporting
Once you have checked a set of arguments and
found an error, what should you do?
Sometimes you can return an “error value.”
Example:
// ask user for a yes-or-no answer;
// return 'b' to indicate a bad answer (i.e., not yes or no)
char ask_user(string question)
{
cout << question << "? (yes or no)\n";
string answer = " ";
cin >> answer;
if (answer =="y" || answer=="yes") return 'y';
if (answer =="n" || answer=="no") return 'n';
return 'b'; // ‘b’ for “bad answer”
}
// calculate area of a rectangle;
// return –1 to indicate a bad argument
int area(int length, int width)
{
if (length<=0 || width <=0) return –1;
return length*width;
}
That way, we can have the called function do the detailed checking, while letting each caller handle the error as desired.
This approach seems like it could work, but it has a couple of problems that make it unusable in many cases:
- Now both the called function and all callers must test. The caller has only a simple test to do but must still write that test and decide what to do if it fails.
- A caller can forget to test. That can lead to unpredictable behavior further along in the program.
-
Many functions do not have an “extra” return
value that they can use to indicate an error.
For example, a function that reads an integer
from input (such as
cin
’s operator>>
) can obviously return anyint
value, so there is noint
that it could return to indicate failure.
There is another solution that deals with this problem: using exceptions.
5.6 Exceptions
If a function finds an error which it cannot handle,
it does not return normally. It throws an exception
indicating what went wrong.
try
block - lists all kinds of exceptions code needs to handle in the catch parts.catch
block - specifies what to do if called code uses throw.
5.6.1 Bad arguments
class Bad_area { }; // a type specifically for reporting errors from area()
// calculate area of a rectangle;
// throw a Bad_area exception in case of a bad argument
int area(int length, int width)
{
if (length<=0 || width<=0) throw Bad_area{};
return length*width;
}
Bad_area{}
means “Make an object of type
Bad_area with the default value,”
throw Bad_area{}
means “Make an object of
type Bad_area and throw it.”
5.6.2 Range errors
vector<int> v; // a vector of ints
for (int i; cin>>I; )
v.push_back(i); // get values
for (int i = 0; i<=v.size(); ++i) // print values
cout << "v[" << i <<"] == " << v[i] << '\n';
The termination condition is i<=v.size()
rather than the correct i<v.size()
.
It is an example of an off-by-one error.
5.6.3 Bad input
Consider reading a floating-point number:
double d = 0;
cin >> d;
Testing if the last input operation succeeded by
testing cin
if (cin) {
// all is well, and we can try reading again
}
else {
// the last read didn’t succeed, so we take some other action
}
One possible reason for operation failure is that
there wasn’t a double
for
>>
to read.
double some_function()
{
double d = 0;
cin >> d;
if (!cin) error("couldn't read a double in 'some_function()'");
// do something useful
}
The condition !cin
means that the
previous operation on cin
failed.
5.6.4 Narrowing errors
Narrowing errors are errors when we assign a value
that’s “too large to fit” to a variable, it is
implicitly truncated.
int x = 2.9;
char c = 1066;
Since ints
don't have fractional values
of an integer, x will get the value 2 rather than
2.9.
And c
will get the value 42
(representing the character *) as per ASCII
character set.
5.7 Logic errors

After removing initial compiler and linker errors, logic errors occur when the program runs but produces wrong output.
int main()
{
vector<double> temps; // temperatures
for (double temp; cin>>temp; ) // read and put into temps
temps.push_back(temp);
double sum = 0;
double high_temp = 0;
double low_temp = 0;
for (int x : temps)
{
if(x > high_temp) high_temp = x; // find high
if(x < low_temp) low_temp = x; // find low
sum += x; // compute sum
}
cout << "High temperature: " << high_temp << '\n';
cout << "Low temperature: " << low_temp << '\n';
cout << "Average temperature: " << sum/temps.size() << '\n';
}
For values,
76.5, 73.5, 71.0, 73.6, 70.1, 73.5, 77.6, 85.3,
88.5, 91.7, 95.9, 99.2, 98.2, 100.6, 106.3, 112.4,
110.2, 103.6, 94.9, 91.7, 88.4, 85.2, 85.4, 87.7
The output produced was,
High temperature: 112.4
Low temperature: 0.0
Average temperature: 89.2
Since low_temp
was initialized at
0.0
, it would remain 0.0
unless one of the temperatures in the data was
below zero.
5.8 Estimation
Estimation is a noble art that combines common
sense and some very simple arithmetic applied to a few
facts.
Also sometimes humorously called as guesstimation.
Always ask yourself these questions:
- Is this answer to this particular problem plausible?
- How would I recognize a plausible result?
Here, we are not asking,
“What’s the exact answer?” or
“What’s the correct answer?”
That’s what we are writing the program to tell us.
5.9 Debugging

When you have written some code, you have to find and
remove the errors. That process is usually called
debugging and the errors are called bugs.
Debugging works roughly like this:
- Get the program to compile.
- Get the program to link.
- Get the program to do what it is supposed to do.
Debugging the most tedious and time-wasting aspect of programming and will go to great lengths during design and programming to minimize the amount of time spent hunting for bugs.
5.9.1 Practical debug advice

Make the program easy to read so that you have a
chance of spotting the bugs:
- Comment your code well
- The name of the program
- The purpose of the program
- Who wrote this code and when
- Version numbers
- What complicated code fragments are supposed to do
- What the general design ideas are
- How the source code is organized
- What assumptions are made about inputs
- What parts of the code are still missing and what cases are still not handled
- Use meaningful names
- Use a consistent layout of code
- Break code into small functions, each expressing a logical action
- Avoid complicated code sequences
- Try to avoid nested loops, nested if-statements, complicated conditions
- Use library facilities rather than your own code when you can
- A library is likely to be better thought out and better tested than what you could produce as an alternative while busily solving your main problem
5.10 Pre- and post-conditions

Pre-condition Example:
int my_complicated_function(int a, int b, int c)
// the arguments are positive and a < b < c
{
if (!(0<a && a<b && b<c)) // ! means “not” and && means “and”
error("bad arguments for mcf");
// . . .
}
int x = my_complicated_function(1, 2, "horsefeathers");
Here, the compiler will catch that the requirement
(“pre-condition”) that the third argument be an integer
was violated.
Basically, what we are talking about here is what to do
with the requirements/pre-conditions that the compiler
can’t check.
5.10.1 Post-conditions
// calculate area of a rectangle;
// throw a Bad_area exception in case of a bad argument
int area(int length, int width)
{
if (length<=0 || width <=0) throw Bad_area();
return length*width;
}
It checks its pre-condition,
but it doesn’t state it in the comment
(that may be OK for such a short function) and
it assumes that the computation is correct
(that’s probably OK for such a trivial computation).
However, we could be a bit more explicit:
int area(int length, int width)
// calculate area of a rectangle;
// pre-conditions: length and width are positive
// post-condition: returns a positive value that is the area
{
if (length<=0 || width <=0) error("area() pre-condition");
int a = length*width;
if (a<=0) error("area() post-condition");
return a;
}
We couldn’t check the complete post-condition, but we checked the part that said that it should be positive.
5.11 Testing

Testing is a systematic way to search for errors.
“The last bug” is a programmers’ joke.
There is no “the last bug” in a large program.
Testing includes comparing the results to what is
expected by executing a program with a large and
systematically selected set of inputs.
Test Yourself!

- The four major types of errors are...
- header errors, variable errors, function errors and class errors
- compile-time, link-time, run-time, and logical
- object errors, inheritance errors, template errors and polymorphism errors
- A linker error would occur when...
- A function used in your source code can't be found in any linked file or library.
- A variable should be linked by assignment to another variable but it is not.
- A link between one class and another is missing.
- An example of a run-time error is when...
- you send the wrong type of argument to a function.
- you meant to add tax to the sales price but you subtracted it instead.
- your program tries to divide by zero.
- you forget the semi-colon at the end of a line of code.
- An example of a logic error is when...y
- you send the wrong type of argument to a function.
- you meant to add tax to the sales price but you subtracted it instead.
- your program tries to divide by zero.
- you forget the semi-colon at the end of a line of code.
- One reason why throwing an exception is better than returning an error value is...
- the exception cannot be ignored.
- throwing an exception makes the code that produced the error also handle it.
- throwing exceptions is a more modern style.
- An example of something student programs are not expected to handle is:
- bad input
- hardware failures
- divide-by-zero errors
- Most large programs...
- will always contain some bugs.
- can be fully de-bugged in a couple of days.
- will likely be bug-free from the start.
- Which is used to handle the exceptions in c++?
- exception handler
- catch handler
- none of the mentioned
- handler
- Which of the following does not cause a syntax error to be reported by the C++ compiler?
- Extra blank lines
- Missing ; at the end of a statement
- Missing */ in a comment
- Mismatched {}
- Which of the following is not a syntax error?
- std::cout << "Hello world! ";
- std::cout << 'Hello world! ';
- std::cout << "Hello world! ";
- Run Time Errors are ..
- the errors which are traced by the compiler during compilation, due to wrong grammar for the language used in the program
- the errors encountered during execution of the program, due to unexpected input or output
- the errors encountered when the program does not give the desired output
- What will happen when the exception is not caught in the program?
- error
- program will execute
- none of the mentioned
- block of that code will not execute
Answers
1. b; 2. a; 3. c; 4. b; 5. a; 6. b; 7. a; 8. a; 9. a; 10. c; 11. b; 12. a;
Drill
