Bugs and Basic Program Debugging

Time: 00:09:03 | Download: Large Small | Streaming | Slides (PDF)

If debugging is the process of taking bugs out of a program, then programming must be the process of putting bugs into a program. There are many different kinds of bugs that programmers can put into a program:

  1. Syntax
  2. Link
  3. Logical
  4. Runtime
  5. Task synchronization
  6. Heisenbug

Only the first four kinds of bugs are considered this semester. Syntax and link errors are detected at compile time by one part of the compiler system. Specifically, the compiler component, the middle part of the compiler system, provides diagnostic messages that help programmers locate and identify syntax errors. Link errors are detected by the linker (or loader on some platforms), the last part of the compiler system. Logical and runtime errors are not detected by the compiler system and are typically much more difficult to locate. Visual Studio has a built in debugger that is very useful for locating these errors and it is introduced in the following sections.

Syntax Errors

A correct program consists of a sequence of keywords, symbols, and identifiers (an identifier is a name that a programmer gives to a programming element such as a variable, function, class, etc.). The sequence must be flexible enough to allow programmers to write the various programs needed to solve new problems but it must also follow a well-defined pattern that can be translated in to the machine code that a computer can run. This pattern is specified by each specific programming language and represents the language's syntax. Any deviation from this pattern represents a syntax error.

Every compiler, specifically the compiler component, will detect syntax errors when compiling the program. However, the error messages or diagnostics will vary greatly between different compilers. Sometimes the error messages are very accurate, while at other times they provide little more than a starting point for your debugging session. Additionally, some systems, such as Visual Studio, provide a syntax checker that runs in the background while you write code. Studio's syntax checker, IntelliSense, underlines potential syntax errors with a red wavy line. After correcting a syntax error, it may be necessary to "click" the mouse pointer to the end of the corrected statement to allow IntelliSense to "catch up" and remove the underlining - it is even sometimes necessary to save the file to trigger the update.

A screen capture indicating the semicolon at the end of the cout statement is going to be deliberately removed to create a syntax error. A screen capture showing the 'return' statement underlined with a red wavy line - the line above is missing the semicolon at the end.
(a)(b)
A screen capture showing the output window after it has been scrolled to the top. It shows the file name and line number where the syntax error was detected. It also displays: syntax error : missing ';' before 'return'.
(c)
A screen capture of the editor window with a pointer indicating the line on which the syntax error was detected.
(d)
Visual Studio error detection and reporting. An illustration of IntelliSense and the error output from Visual Studio.
  1. Intentionally creating a syntax error by removing a semicolon from the "Hello, World!" program.
  2. IntelliSense underlines the the "return" statement - this is NOT the syntax error but is the point where IntelliSense detects the error.
  3. The output window (usually at the bottom of Visual Studio) after attempting to build the hello world program with a missing semicolon. The error is reported on line 7 but is actually at the end of line 6. Errors may occur above the line on which they are reported but are never below.
  4. Double clicking on an error in the output window (c) will cause Studio to display the file containing the error, will move the cursor to the line where the error is detected, and will mark the reported line with a pointer (circled in red).

When Studio attempts to build a project that contains syntax errors, it reports those errors in the "Output" window (Figure 1(c)), which is generally at the bottom of the Studio IDE. To most effectively use the information contained in the output window, follow these five "rules of thumb:"

Five Rules of Thumb: Using The Output Window Error Information

  1. One syntax error can be detected more than once and it can cause multiple problems in the program. So it is always best to start at the top of the output window (i.e., with the first error) and to work down one error at a time. With some errors and with experience, you will be able to work with more than one error at a time, but for now, it's best to fix the problems one at a time.
  2. The fastest way to locate an error in the text editor is by the error message in the "Output" window at the bottom Visual Studio (you many need to select the "Output" tab). Scroll to the error message describing the error you wish to correct next (the line highlighted in blue in Figure 1(c)) and double-click the message. The file containing the error will move to the front in the editor and the reported line will be marked with a pointer along the left edge (Figure 1(d)). This fast navigation "trick" is of little use with tiny programs like hello world but becomes very helpful as programs increase in size and become spread over multiple files.
  3. The compiler reports where it first locates the error, which may not be the point at which the error was made. So begin with the reported line (the line to which you are taken when you double clicked the error in the output window). If the error is not on that line, then work backwards (i.e., upward) one line at a time until you find the error. Note that the error will never be below the reported line number.
  4. The keywords and symbols that form a language's syntax act like guideposts during the compilation process: as long the correct syntax element is found where it is expected, the compiler "knows where it is," but the compiler can "get lost" when there is a syntax error. While the compiler is lost, it cannot check the syntax of the code that it is reading. Eventually, the compiler will "see" a major signpost (like the beginning of a function) and "figures out where it is" and resumes checking the syntax, which is called error recovery. The implication of this process is if you correct a syntax error, recompile the program, and see MORE errors than you you did before the correction, DON'T put the error back - the compiler is just checking code that it was not able to check before.
  5. Save yourself some time: I have helped students in the past that have reported Googling the error number (e.g., C2143 or LNK2019) that appears as a part of the error message in the output window. It is very rare that doing this will result in more information than what is already presented in the output window. Instead, spend your time and effort learning how to read C++ and how to spot the details that are wrong or out of place.

For the example above, the error message clearly identifies the problem: Missing ';' before 'return'. However, for most programming errors the message is frequently misleading and is sometimes utterly incomprehensible! To see an example of this, rewrite the output statement as follows:

cout << "Hello World" >> endl;

And then rebuild the project. This single error generates many lines of diagnostics in the output window. The main error message, the one that you would double click to jump to the error in the editor, tells us little about the error (the single line from the output window is wrapped for better display):

1>e:\tmp\cs1410 past\hello\hello\hello.cpp(6): error C2784:
'std::basic_istream<_Elem,_Traits> &std::operator >>(std::basic_istream<_Elem,_Traits> &&,_Ty &)' :
could not deduce template argument for 'std::basic_istream<_Elem,_Traits> &&' from 'std::basic_ostream<_Elem,_Traits>'

Fortunately, IntelliSense does underline the >> operator so the programmer has a better idea of where the problem is located, but beyond identifying the location, error messages often tell the programmer very little. Correcting the problem depends on the programmer knowing what the code means and what the correct C++ syntax should be.

Link (LNK) Errors

Link errors arise from the linker, the last component or program of the compiler system to run. Although there are many causes of link errors, only one is common when writing the programs appearing in the first few chapters. The problem represents a user error that is very easy to make but is also easy to correct. To demonstrate this problem, begin with the following simple and correct program:

#include <iostream>
using namespace std;

int main()
{
	int	number;
	cout << "Please enter a number: ";
	cin >> number;

	return 0;
}
  1. Build the program
  2. Run the program (without debugging)
  3. With the console window still open (i.e., don't enter a value at the prompt and don't press the enter key), make a non-significant change (e.g., add an empty line at the bottom of the program)
  4. Save and rebuild the program
  5. Run the program again
(a)(b)
1>------ Rebuild All started: Project: Error3, Configuration: Debug Win32 ------
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppClean.targets(75,5):
	warning : Access to the path 'E:\TMP\CS1410 PAST\ERROR3\DEBUG\ERROR3.EXE' is denied.
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.CppClean.targets(75,5):
	warning : Access to the path 'E:\tmp\cs1410 past\Error3\Debug\Error3.exe' is denied.
1>  Error3.cpp
1>LINK : fatal error LNK1104: cannot open file '****************.exe'
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
(c)
A link error caused by a busy file.
  1. Code used to demonstrate a common link error.
  2. Steps to create the link error.
  3. The error message as it appears in the output window (the second and third lines are wrapped to improve how they display here). The highlighted line is the key to solving the problem: it states that it is a LINK error (also, the error number begins with LNK). Unlike syntax errors, link errors are not detected while processing the source code files, so you CANNOT double-click the error in the output window and jump to the error location in the editor. The last part of the error message states "cannot open file *.exe" (the file name is replaced by a asterisks as it will vary).

When the compiler system successfully compiles a program, the linker will create a new executable file whose name ends with a .exe extension. In this example, when the linker tries to create the new executable file it is unable to remove the old file because the program is busy (i.e., it is still running and in use). The solution is simply to close the previous console window before rebuilding the program. This error usually occurs when you iconify the console window while you continue to work on the code and then forget to close the window before recompiling the program.