Previous Table of Contents Next


Chapter 2
Common Mistakes and How to Avoid Them

Deck: Get a clear look at some classic surprises you'll want to avoid in C programs
by Paul Conte

Why do some programmers think C is such a hot language? It must be because it has burned them so many times. Unless you're from the "no flame, no gain" school of programming, you need to watch out when you start using C. In this book, I point out some of the "hot" spots you really want to avoid.

Let's start by firing up an example.

  if (x = y)
     printf("Equal values");

Simple enough. If y is not zero, print "Equal values". And, by the way, replace the value of x with the value of y. Isn't it nice that C lets you do assignment within an if statement expression?

But maybe you thought this code really meant: If x is equal to y, print "Equal values"? No, the code for that is

  if (x == y)
     printf("Equal values");

If this example tripped you up, don't worry. Typing = (assignment) instead of == (equality) occasionally gets the best C programmers, too. The problem isn't in comprehending the different meanings of = and ==. The problem is that it's easy to mistype = when you mean ==, especially because = is the standard mathematical symbol for equality, and = represents equality in many other widely used programming languages (e.g., PL/I, COBOL, and Pascal). Unfortunately, C treats this easy-to-make typo as an intentional assignment operation. The resulting code will execute, and the error may be hard to diagnose.

Hard-core C programmers may try to convince you it's your inexperience, not C's syntax, that causes this type of coding error. But there's a booming market in C source-code checkers (known as "lint" filters) to help experienced C programmers protect themselves from just these kinds of sneaky problems. If C's pitfalls weren't so pervasive, lint utility vendors would be out of business.

All programmers are not created "equal equal," so if you want to be an A++ C programmer (why be just a C++ programmer?), the first rule is don't use assignment in an if statement expression, unless it is absolutely necessary. In addition, use a compiler warning level or a lint utility that will catch = in if statement expressions. Be forewarned, however, that you may never be acknowledged as a "real" C programmer unless you're willing to take some risks to speed up your code by a few nanoseconds. Another good technique -- if you can handle accusations of "wimp" programmer -- is to define the macro

   #define EQ ==

and never use == at all. Instead, you can write logical expressions, such as

if (x EQ y)
   printf("Equal values");

In addition to = and ==, C also has & (bitwise AND), && (logical AND), | (bitwise OR), and || (logical OR). The bitwise and logical operators work the same, when their operands are 0 or 1. In other cases, however, the results are different. For example,

   2 && 4

is 1, which is considered "true" in an if statement, whereas

   2 & 4

is 0, which is "false." Because, in many cases, & and | produce the same effect as && and || in if statement expressions (i.e., zero or non-zero), incorrect use of the bitwise operators can cause infrequent and hard-to-diagnose errors. If you'd rather rely on something more than luck for correct programs, you may want to define the following four macros and use them instead of &, |, &&, and ||.

   #define  and( a, b )  ( ( a ) & ( b ) )
   #define  or( a, b )  ( ( a ) | ( b ) )

   #define  AND  &&
   #define  OR  ||

Lazy Logic

Yes, C is a devilishly clever little language. It's quick to write, too. Suppose you've written a function, get_customer, to return either an integer customer ID or zero if no customer is input. Why ywaste time with "verbose" code like

  custid = get_customer();
  if (custid > 0) {
     /* Process the customer */
  }

when you can simply write:

  if (custid = get_customer()) {
      /* Process the customer */
}

With the original definition of get_customer, this code works. In C, an if statement evaluates the expression within parentheses, and, if the expression's value is non-zero, the subordinate code is executed. In this example, the variable custid is set to the return value of get_customer. Because the value of a C assignment operation is the same as the value assigned to the target variable, when custid is assigned a non-zero value, the subordinate code to process the customer is executed.

You'll see "simplified" if statement expressions like this all over C programs. But suppose you and your fellow programmers have been using get_customer for a while; say you have a dozen or so programs that call it. Then one day you get an I/O error that zaps one of your programs, and you decide you had better add to get_customer a return value of -1 for an I/O error. Problem solved? No, problems are created. Every

  if (custid = get_customer())

statement will still execute the subordinate code when there's an error because the value of the if statement expression is non-zero. On the other hand, if you follow the first rule and keep the assignment operation separate, your code will work properly with the new error return value.

C is a "truth-or-consequences" language. You'll experience less of the latter if you use only logical expressions (ones that evaluate to 0 or 1) in if statements. You can define the following simple macros to implement Boolean variables and functions that return a Boolean value.

  #define BOOL   int
  #define TRUE   1
  #define FALSE  0

You should also use only Boolean variables and functions with the logical operators && and ||. Following this practice eliminates problems caused by accidentally using the bitwise operators & and | in logical expressions.


Previous Table of Contents Next