"The term bug to describe a defect has been engineering jargon since at least as far back as the 1870s, long before electronic computers and computer software. ...
"U.S. Navy Rear Adm. Grace Hopper, a computer pioneer, popularized a story about a moth that caused a problem in an early electromechanical computer. While Hopper was working on the Mark II and Mark III as Harvard faculty in about 1947, operators traced an error in the Mark II to a moth trapped in a relay. The moth was removed from the mechanism and taped in a log book with the note 'First actual case of bug being found.'"
wikipedia.org
If debugging takes bugs out of a program, programming must put bugs into a program. There are many different kinds of bugs that programmers can and do put into their programs:
This section treats the first two errors, while the following sections address logical and run-time errors.
A program consists of a sequence of keywords, symbols1, and identifiers (an identifier is a name that a programmer gives to a programming element such as a variable, function, or class). The rules specifying the correct and acceptable order of the keywords, symbols, and identifiers form a programming language's syntax. Syntax must be flexible enough to let programmers write programs to solve new problems, while being well-defined enough for the compiler to translate programs that adhere to it into machine code that computers run. Any deviation from the syntax rules represents a syntax error.
The C++ compiler component detects syntax errors and displays diagnostic messages while compiling a program. However, the helpfulness of the diagnostics depends on the specific error and on the compiler. Sometimes, the error messages are very specific and accurate, but other times they only indicate that the compiler detected an error earlier in the program. To illustrate compiler diagnostics, the following examples deliberately omit the semicolon at the end of a statement.
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, World!" << endl
return 0;
} |
dab@Neptune:/tmp$ g++ hello.cpp hello.cpp: |
| (a) | (b) |
![]() |
![]() |
| (a) | (c) |
![]() |
|
| (b) | |
Syntax errors are necessarily the first errors programmers must correct; fortunately, they are (generally) the easiest to locate and identify. For example, the diagnostics above clearly identify the syntax error as a missing or expected ';' before 'return'. However, some compiler systems occasionally produce unhelpful diagnostics, while others are misleading, and some are utterly incomprehensible! In the early 1980s, I witnessed a C compiler that "helpfully" reported an error as: "Bad Tuna." (The compiler was an early university product, and, I suspect, the message was intended to help the compiler-writer - who neglected to remove the output before releasing the compiler - debug the compiler itself rather than application programmers using it.) Follow these guidelines to locate and correct syntax errors effectively.
The diagnostics illustrated in the previous figures include the file name and line number where the compiler detects the error, not necessarily where it occurs. This information is of limited value for small programs, but it becomes very helpful as programs grow and span multiple files. All "good" programming editors provide at least one way to navigate to the reported location quickly. However, the compiler reports where it first locates the error. The compiler can detect some syntax errors - such as a missing semicolon - very quickly, but may fail to detect other errors until it has processed many lines of code. Consequently, the error may be many lines above the reported location, but it is never below the reported line. Therefore, the most efficient strategy is to begin on the reported line and work backward until you locate the error.
The syntax elements - keywords, symbols, and identifiers - act like signposts during the compilation process. While the compiler finds the correct syntax elements where it expects them, it "knows where it is." However, it can "get lost" when there is a syntax error - a missing or incorrect "signpost." While the compiler is lost, it cannot validate the program's syntax, so it skips ahead. Sometimes it "recognizes" a major signpost (like the beginning of a function) and "can figure out where it is." Once it "knows where it is," it resumes validating the program's syntax - a compiler feature called error recovery. Error recovery doesn't correct the error, nor does the compiler generate additional machine code - only the syntax checker recovers from the error and continues validating the program's syntax from the recovery point onward.
Although error recovery produces additional validation and diagnostic information, it also creates a potential pitfall for new programmers. In some cases, a programmer may correct a syntax error and recompile a program only to see the number of syntax errors increase. The increase happens because the compiler processes code it previously skipped when it became "lost" after encountering the now-corrected syntax error. So, assuming the correction is valid, the increased error count still represents progress towards a syntactically correct program.
C++ utilizes a one-pass compiler that opens each file one at a time and processes its contents once, from beginning to end. Unsurprisingly, it displays syntax diagnostics in the order it detects them. However, a single syntax error can trigger multiple diagnostic messages, sometimes interleaving diagnostics from subsequent syntax errors. Consequently, it is most efficient to correct syntax errors in the order they are displayed. Depending on the compiler and operating system, you may need to scroll the output or console window, or use a command-line utility such as more to find the first diagnostic messages. Depending on the errors (and with experience), you can fix multiple errors at once, but initially it's best to fix problems one at a time.
Some compilers, Visual Studio, for example, include obscure error codes in the diagnostic messages. Students report fruitless online searches for these codes, wasting time and effort. Furthermore, as the following figure illustrates, the codes change over time. So, even a successful search may be more confusing than helpful if it's out of date. A superior and more efficient approach is a thorough understanding of C++ syntax.
cout << "Hello World" >> endl;
(a)
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>'(b)
1>e:\tmp\cs1410\Hello\Hello\hello.cpp(6,26): error C2676: binary '>>': 'std::basic_ostream<char,std::char_traits<char>>' does not define this operator or a conversion to a type acceptable to the predefined operator(c)
The linker or loader is the final stage of the full C++ compiler system. It assembles the application program object files, any used library functions, and a run-time file into the final executable program. Assembly errors occur when the linker/loader cannot complete the assembly process and fails to create the executable. (It's more common to refer to these as link, linker, or loader errors; however, the text adopts "assembly" to generalize the error to various operating systems.) This section addresses a relatively minor problem that causes an assembly error.
#include <iostream>
using namespace std;
int main()
{
int number;
cout << "Please enter a number: ";
cin >> number;
return 0;
} |
|
| (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) | |
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 can't remove the old file because the program is "busy" (i.e., it is still running and in use). Simply closing the previous console window before rebuilding the program resolves the error. This error usually occurs when you iconify the console window while continuing to work on the code and then forget to close the window before recompiling the program.