-----

Mystery Errors

-----

Some C/C++ errors are reasonably self-explanatory: compiler errors such as "undefined variable" or run-time errors such as "divide by zero". The compiler will tell you where the error occurred; run-time errors can be isolated using print statements.

In some situations, however, the C/C++ compiler and run-time system provide little or no help in isolating the error. Or, the error confuses C/C++ to where it reports errors in places other than the actual source of the problem. This file documents such errors.

If I haven't included some of your favorite mystery errors, please send them to me ( Geoff Kuenning).

Compiler errors

Innocuous warnings

G++ will generate a warning if you compare a signed number to an unsigned one (e.g. using ==). This rarely indicates a programming bug. It probably means that you are comparing two integers, which happen to be stored in an int and a char. To prevent the warning, cast the char to an int. Or set a temporary int variable equal to the char and use that variable in the test.

General purpose techniques

If you have some idea what's wrong, apply any "smart" debugging methods that seem to apply. The following methods are intended for situations where you haven't got a clue what's wrong and you need to isolate the bug so you can focus your attention.

The best method is to make a copy of the offending program. This allows you to make drastic changes (e.g. deleting major sections of the code) to help isolate the bug, without fear of damaging your almost-working program.

In some cases, the problem may appears and disappears when you add/remove code that should not be involved: code that is known to work, code that is unrelated to the error, print statements, declarations of unused variables, .... Such behavior suggests that some part of your program is accessing areas of memory that it should not be touching. See the following sections (below):

Petty syntax errors with dire consequences

A common error is to use = (assign) in place of == (equal) in the test for a conditional statement or a loop. This is often syntactically legal, because C/C++ does not distinguish integers from booleans. In the typical version of this error, the test is something like "myvar=0" instead of "myvar==0" This assignment statement sets myvar to be 0 and then returns the value 0. So the test is always false and, moreover, the value of myvar is reset to 0.

Missing semi-colons in certain positions confuse the compiler, causing it to report spurious errors elsewhere in the code. Missing semi-colons are particularly hard to see when they would occur next to a curly bracket. For example:

Segmentation Faults and Bus Errors

In CS70, the most common causes of segmentation faults and bus errors are a failure to follow two simple rules given in class:

  1. Things allocated with new foo should be deleted with plain delete, while things allocated with new foo[] should be deleted with delete[].
  2. ALWAYS either define or disable the assignment operator and copy constructor for each class you write. If you define them, be sure that they do a deep copy, not a shallow one. If you don't want to bother to write them, or plan to write them later, disable them by declaring them as private. Never leave it up to the compiler.

Segmentation Fault

A segmentation fault means that you have tried to access a memory address which is not part of the data space assigned to your process. In other words, you've done something illegal with a pointer. Common examples include:

Sometimes these errors cause C to access a piece of memory which belongs to you, but not to the object you intended. In such cases, your program may behave in mysterious (and very diverse) wrong ways before crashing, usually with a segmentation fault. On a multi-user system, and often on a single-user one, the weird behavior will be slightly different if you run the program again.

Bus Error

Officially, this means that an invalid address was issued from the processor to the memory bus. That doesn't translate easily into information about what's wrong with your code! Common ways to get this error are:

Passing streams by value

Always pass streams by reference. Mysterious bad things happen if you pass them by value. The errors may appear to relate to parts of the code unrelated to the input/output streams.

Unsigned and signed integers

If you combine a signed integer and an unsigned integer, the result is unsigned. If you combine two unsigned integers, even with minus, the result is unsigned. In unsigned arithmetic, values "wrap around" when you go below zero or above the largest possible value. So 3 - 5 = 254 if the two input values were stored in unsigned char variables. Frequently, this is not what you intended.

To avoid this problem, explicitly cast unsigned numbers to signed numbers or store them in signed variables, before attempting to do normal (signed) arithmetic with them. If you aren't sure whether an explicit conversion is necessary or not, do it as a precaution.

If the variable declaration doesn't explicitly say signed or unsigned, most numerical types (e.g. int, long) default to signed. On the whole, unsigned numbers are rare.

However, char variables are frequently unsigned. If you don't specify whether char is signed or unsigned, it could be either, depending on your C/C++ implementation. And char variables are frequently used to hold small positive integers (e.g. colormap values) and therefore declared as signed.

Function declarations

Within each code file, each function must be declared before the first time it is called. Otherwise, the compiler will just assume some dumb declaration (e.g. the return type is int) and complain that either (a) your use of the function has bad typing or (b) that a later declaration of the function is inconsistent.

There are two ways to solve such errors. You can reorder the definitions of your functions so that each function is defined before it is first called. Alternatively, you can put a short declaration of the function (just giving the types of inputs and outputs but no code) at the start of the file. This allows the full definition to be placed anywhere in the file or, indeed, in another file.

If you have a short declaration at the start of the file and the full code later, and the compiler complains, check that the types in your declaration are consistent with your later definition.

Shadowing

Each variable should be declared only once in each function. The variable can be reset (including allocating new space for a pointer variable) in the middle of the function. However, these later assignments should not specify the type of the variable.

Redeclaring a variable creates a NEW variable. This new definition blocks your access to the old definition. This is almost never what you intended and will cause the program to misbehave in mysterious ways. However, redeclaring a variable is not officially an error, because it is possible to construct situations in which redefining a variable can look like a feature.

Despite the fact that almost all redeclarations represent bugs, g++ does not even generate a warning.


This page is maintained by Geoff Kuenning.