Typos that Compile

It is also possible to get unexpected behavior by making a typo that is not picked up by the compiler. This can happen when a typo causes a statement to look like a different, but equally valid, statement.

Difference Between Mathematical Statements and C/C++ Statements

Some mathematical expressions conform to the C/C++ syntax, but have a different meaning in C/C++ than they do in mathematics. This section shows pitfalls related to misconception of notation.

X Between Y and Z

Consider the mathematical expression 7 < x < 16, denoting that the value of x resides between 7 and 16. When this expression is used in C/C++, it does not behave as expected. Listing 15.7 demonstrates:

Code Listing 15.7. Incorrect Use of a Mathematical Expression
int x = 32;
if (7 < x < 16)
{
    printf("x has a two nibble
								
								 value.");
}

Instead of printing x has a two nibble value when the value of x resides between 7 and 16, the piece of code in Listing 15.7 first evaluates 7 < x, to which the answer is either true (1) or false (0). Then this 0 or 1 answer is compared to 16, inevitably resulting in true (1).

Power of X

In some programming languages (Pascal, for instance) the ^ character is used to denote the power of a variable. In C/C++ this character is used to denote a bitwise exclusive or (xor), which is why some programmers will fall for the following pitfall:

// a becomes 2.
int a = 0, x = 0;
a = x^2;

Instead of a receiving the value of x to the power of 2 (which is 0), it receives the value 2. This is because xor-ing 0 with 2 makes 2. Use the pow() function in C/C++ to get a power of a number.

In order to make large numbers easier to read, it is customary to space digits in groups of three with a comma or period separating the groups. For instance, ten thousand is written as 10.000 or 10,000 depending on which country you live in. This, however, is not done in C/C++. Listing 15.8 demonstrates how this can lead to a pitfall.

Code Listing 15.8. Incorrect Use of Digit Grouping
double x;
for (x=0; x < MAXRANGE; x += 1,500)
    printf("X= %f n", x);

Instead of x increasing by fifteen hundred on each iteration, it is increased merely by one. This means that the number of effective iterations is MAXRANGE instead of MAXRANGE/1,500. The compiler sees the numbers after the comma as a new statement. This may seem strange, but the following three (meaningless) statements compile also.

5;4;
500;

Inadvertently Creating Labels

Sometimes typos cause the compiler to interpret a statement differently than expected. Listing 15.9 shows how a simple typo can cause the wrong method to be called.

Code Listing 15.9. Typo Causing the Wrong Method to be Called
#include <ostream.h>

class base
{
public: void Info() { cout << "BaseInfo." << endl;}
} ;

class derived: public base
{
public:
    void Info() { cout << "DerivedInfo." << endl;}
    void GiveInfo(int a)
    {
        if (a)
            base:Info();
        else
            Info();
    }
} ;

void main(void)
{
    derived der;

    der.GiveInfo(0);
   
							
							 der.GiveInfo(2);
}

It looks like the author of Listing 15.9 is trying to invoke two different versions of the Info() method by calling the method GiveInfo() with two different values (first 0 and then 2). The method GiveInfo() decides to call the Info() method of the base class or the Info() method of the derived class, based on the value of its input parameter a. Sadly, the output of this program is:

DerivedInfo.
DerivedInfo.

Why? Because the author made a typo and instead of calling the base class method Info() by typing base::Info(), he made a label called base and called the local Info() method by typing base:Info()(note the missing second colon!). Because the object der is of class derived, the Info() method of derived is called in both cases. A good way to avoid these kinds of typos is to set the compiler warning level so that it warns against the definition of unused labels.

Inadvertently Creating Comments

Listing 15.10 shows another example of how a new statement can be created via a typo. This time a calculation is transformed into something new.

Code Listing 15.10. Typo Causing the Wrong Calculation to Be Performed
#include <string.h>

char data[200] = "How many tabs can fit in this line?";

void GetTabInfo(int *tabsize)
{
    int tabsline = 0;

    tabsline = strlen(data)/*tabsize;
    int linediff = strlen(data) /* subtract diff*/ -
							
							 (*tabsize*tabsline);
}

In Listing 15.10, the author is trying to determine how many tabs would fit in a given line by dividing the number of characters in that line by the size of a tab (tabsline = strlen(data)/*tabsize;). However, by omitting a space between the division operator / and the scope operator *, the author has inadvertently created the start of a multiline comment /*. This comment ends with the next occurrence of */, and so the statement actually seen by the compiler is not:

tabsline = strlen(data)/*tabsize;

but:

tabsline = strlen(data)-(*tabsize*tabsline);

A good way to avoid these kinds of typos is to use source editors that change the colors of comment lines.

Inadvertently Causing Loop Problems

Other annoying typos, which can have disastrous effects and can be difficult to spot, are placing a semicolon directly after a loop definition and omitting the { } characters around a loop body.

// Loop without statements.
for (i = 0; i < MAX; i++);
    cout << i++;

The preceding loop has no statements. Consequently the cout << i++ statement is executed only once, changing i from MAX to MAX+1.

// Never ending loop.
while (i < MAX);
    cout << i++;

The preceding loop also has no statements. Consequently, the cout << i++ statement is executed only once, when i happens to be smaller than MAX. In all other cases the while becomes a never-ending loop, locking the program (or thread) in which it is executing.

// Loop with a single statement.
for (i = 0; i < MAX; )
    cout << i;
    i++;

The preceding loop also has only one statement, this time because the for is not implemented with a compounded body, which is why the statement i++ is not part of the for loop. Consequently, the preceding loop either does not execute or executes without end. These kinds of bugs often arise when a loop with only one statement is extended. This is why it is a good programming practice to give all loops a compounded body, even those with only a single body statement. This way a developer can never make the mistake of adding body statements where there is no body to begin with.

// Single statement loop.
for (i = 0; i < MAX; i++)
{
    cout << i;
}

When this loop is expanded it can look like this:

// Expanded loop.
for (i = 0; i < MAX; i++)
{
    cout << i;
    cout << " of " << MAX 
							
							<< endl;
}

Mixing Statically Bound Methods with Dynamically Bound Methods

A simple oversight can turn a method that should be dynamically bound (refer to Chapter 8, "Functions," for more information on dynamically binding methods) into one that is staticall y bound, as Listing 15.11 shows.

Code Listing 15.11. Inadvertently Statically Binding Methods
#include <ostream.h>

class A
{
     public:  void print() {  cout << 'A'<< endl; } ;
} ;

class B: public A
{
     public:  void print() {  cout << 'B'<< endl; } ;
} ;

void main ()
{
    // Create a single object
    B anObject;

    // Create two references to the object.
    A *ptr1 = &anObject;

    B *ptr2 = &anObject;

    ptr1->print();
    ptr2->
							
							print();
}

The output of Listing 15.11 looks like this:

A
B

So why do the two pointers, which point to the same instance of B, call two different print() methods? It seems that the type of pointer itself determines which method is called—A* versus B*. The reason for this is that a pointer to an A object knows only of one kind of print() method, namely that of class A. To correctly redefine the print() method for class B, it should be made virtual. By changing the definition of class A to

class A
{
     public:  void virtual print() {  cout << 'A'<< endl; } ;
} ;

The output of Listing 15.11 becomes

B
B

Omitting the virtual keyword (when done unintentionally) can be a bug that is hard to find, because as you have seen, it occurs at runtime depending on how a class is accessed. The methods in Listing 15.11 are all statically bound, compared to the virtual solution in which the methods are dynamically bound.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset