Chapter 26

Ten Common Boo-Boos

In This Chapter

arrow Conditional foul-ups

arrow == v. =

arrow Dangerous loop semicolons

arrow Commas in for loops

arrow Missing break in a switch structure

arrow Missing parentheses and curly brackets

arrow Pay heed to that warning

arrow Endless loops

arrow scanf() blunders

arrow Streaming input restrictions

The programming adventure has its pitfalls. Many of them are common; the same mistakes, over and over. Even after decades of coding, I still find myself doing stupid, silly things. Most often they’re done in haste — usually simple things that I’m not paying attention to. But isn’t that the way of everything?

Conditional Foul-Ups

When you employ an if statement or initiate a while or for loop, you're using a comparison. Properly expressing that comparison is an art form, especially when you try to do multiple things at once.

My advice is to first split up the code before you load everything into the parentheses. For example:

while((c=fgetc(dumpme)) != EOF)

The code on the preceding line works, but before you get there, try this:

c = 1;            /* initialize c */

while(c != EOF)

    c=fgetc(dumpme);

After you know that the code does what’s intended, pack it back into the parentheses.

The situation grows more hair when you combine conditions by using logical operators. I highly recommend that you limit your selection to two choices only:

if( a==b || a==c)

The statements belonging to if are executed when the value of variable a is equal to either the value of variable b or variable c. Simple. But what about this:

if( a==b || a==c && a==d)

Oops. Now the order of precedence takes over, and for the if statement to be true, a must be equal to b, or a must be equal to both c and d. The situation can also be made more clear by using parentheses:

if( a==b || (a==c && a==d))

remember.eps When you can’t remember the order of precedence, use parentheses.

== v. =

A single equal sign is the assignment operator:

a=65;

A double equal sign is used for comparison:

a==65

To get my brain to appreciate the difference, in my head I say “is equal to” when I type two equal signs. Despite that, I still goof up, especially in a conditional statement.

warning_bomb.eps When you assign a value in a conditional statement, you generally create a TRUE condition:

if(here=there)

This if statement always evaluates TRUE, unless the value of variable there is 0, in which case the if condition evaluates as FALSE. Either way, it's most likely not what you intended.

Dangerous Loop Semicolons

You can get into a rut when you’re typing code, using the semicolon as a prefix to typing the Enter key to end a line. That’s dangerous! It’s especially toxic when you code loops:

for(x=0;x<10;x++);

This loop changes the value of variable x to 10. That's it. Any statements that follow, which would normally belong to the for loop, are then executed in order. Ditto for a while loop:

while(c<255);

This loop may spin endlessly, depending on the value of variable c. If the value is greater than or equal to 255, then the loop doesn't execute. Otherwise, it executes forever.

These semicolons are unintentional and unwanted. They’re also perfectly legitimate; the compiler doesn’t flag them as errors. Your editor may warn you inadvertently by not indenting the next line, but that’s it — and you’ll be lucky if you pick up on that hint. I often don’t, just blaming the editor for being stupid.

When you do need to legitimately have a while or for loop with no statements, place the semicolon on the next line:

while(putchar(*(ps++)))

    ;

This sole semicolon on a line by itself shouts to any programmer reading the code that the while loop is intentionally empty.

Commas in for Loops

The three items in a for loop are separated by semicolons. Both of those semicolons are required, and they are not commas. The compiler doesn't like this statement:

for(a=0,a<10,a++)

Because commas are allowed in a for statement, the compiler merely thinks that you've omitted the last two required items. In fact, the following is a legitimate for statement:

for(a=0,a<10,a++;;)

You can write that statement, which assigns the value 0 to variable a, generates a TRUE comparison (which is ignored), and then increments the value of a to 1. Then, because the second and third items are empty, the loop repeats endlessly. (Well, that is unless a break statement belongs to the loop.)

Missing break in a Switch Structure

It's perfectly legitimate to write a switch structure where the execution falls through from one case statement to the other:

switch(letter)

{

    case 'A':

    case 'E':

    case 'I':

    case 'O':

    case 'U':

        printf("Vowel");

        break;

    default:

        printf("Not a vowel");

}

In this example, the first five case conditions capture the same set of statements. That's fine. When you forget the break, however, execution continues falling through with more tests and, eventually, the default. Unless that's what you want, remember to add the break statement.

Suppose that you have a switch structure that's several dozen lines high. One case condition has multiple rows of statements, so many that it scrolls up and off the screen. In that setup, it's easy to forget the break as you concentrate instead on crafting the proper statements. I know — I've done it.

Another situation happens when you code a loop inside a switch structure and you use break to get out of that loop. In that situation, you still need a second break to get out of the switch structure.

tip.eps The Code::Blocks editor lets you collapse and expand parts of your code, but you need to enclose the case statements in curly brackets. Click the – (Minus) button to the left of your code to collapse statements in curly brackets.

Missing Parentheses and Curly Brackets

Forgetting a parenthesis or two is one of the most common C coding mistakes. The compiler will catch it, but usually the error isn’t flagged until the end of the function.

For example, a missing parenthesis in the main() function causes the error to be flagged at the last line in the function. That's a good clue to a missing parenthesis or curly bracket, but it doesn't help you locate it.

Today's editors are good at matching up parentheses and brackets. The Code::Blocks editor actually inserts both characters when you type the first one, on the left. That helps keep things organized. Other editors, such as vim, highlight both sets of brackets when the cursor hovers over one. These helpful features may not be enough to prevent missing bracket errors.

Another editor clue is that the formatting, text coloring, and indents screw up when you forget a parenthesis or bracket. The problem with recognizing this reminder is that the human brain automatically assumes that the editor has screwed up. So you need to train yourself to recognize improper indentation by the editor as a sign of a missing parenthesis or curly bracket.

Pay Heed to That Warning

When the compiler generates a warning, the program (or object code) is still created. That can be dangerous, especially when dealing with pointer errors. The problem is that some warnings can be ignored.

For example, you may be using printf() to display a value that you know is an int, but somehow the compiler insists that it's some other value. If so, you can typecast the variable as an int. For example:

printf("%-14s %5ld %s",

    file->d_name,

    (long)filestat.st_size,

    ctime(&filestat.st_mtime));

In this example, the filestate.st_size variable is of the off_t variable type. The printf() function lacks a conversion character for off_t, so it has typecast it to a long int. (Refer to Chapter 23.) Similar typecasting can be done with other variable types for which printf() lacks a conversion character. But before you go nuts with this trick, check the man page for printf() to ensure that the specific variable doesn't have a conversion character.

check.png A common warning happens when you try to display a long int value by using the %d placeholder. When that happens, just edit %d to %ld.

check.png An "lvalue required" warning indicates that you've written a malformed equation. The lvalue is the left value, or the item on the left side of the equation. It must be present and be of the proper type so that the equation is properly handled.

check.png tip.eps The degree to which the compiler flags your code with warnings can be adjusted. Various flags are used to adjust the compiler’s warning level. These flags are set in Code::Blocks by choosing the Project⇒Build Options command. The Compiler Flags tab in the Project Build Options dialog box lets you set and reset the various warnings.

check.png Generally speaking, the "turn on all warnings" option for a C compiler is the -Wall switch. On the command line, it looks like this:

gcc -Wall source.c

Wall stands for “warnings, all.”

Endless Loops

There’s got to be a way outta here, which is true for just about every loop. The exit condition must exist. In fact, I highly recommend that when you set out to code a loop, the first thing you code is the exit condition. As long as it works, you can move forward and code the rest of the joyous things that the loop does.

Unintentional endless loops do happen. I’ve run code many times, only to watch a blank screen for a few moments. Oops.

tip.eps Console applications, such as the type created throughout this book, can be halted by pressing the Ctrl+C key combination in a terminal window. This trick may not always work, so you can try closing the window. You can also kill the task, which is a process that’s handled differently by every operating system.

scanf() Blunders

The scanf() function is a handy way to read specific information from standard input. It's not, however, ideal for all forms of input.

For example, scanf() doesn't understand when a user types something other than the format that's requested. Specifically, you cannot read in a full string of text by using %s with scanf(). That's because scanf() discards any part of the string after the first whitespace character.

remember.eps Though the fgets() function is a great alternative for capturing text, keep in mind that it also captures the newline that's typed to end standard input. That character, \n, becomes part of the input string.

The other thing to keep in mind when using scanf() is that its second argument is a memory address, a pointer. For standard variable types — such as int, float, and double — you need to prefix the variable name with the &, the address operator:

scanf("%d",&some_int);

The & prefix isn't necessary for reading in a char array — a string:

scanf("%s",first_name);

Individual array elements, however, aren't memory locations, and they still require the & prefix:

scanf("%c",&first_name[0]);

Pointer variables do not require the & prefix, which could result in unintended consequences.

Streaming Input Restrictions

The basic input and output functions in the C language aren’t interactive. They work on streams, which are continuous flows of input or output, interrupted only by an end-of-file marker or, occasionally, the newline character.

When you plan to read only one character from input, be aware that the Enter key, pressed to send the input, is still waiting to be read from the stream. A second input function, such as another getchar(), immediately fetches the Enter key press (the \n character). It does not wait, as an interactive input function would.

check.png tip.eps If you desire interactive programs, get a library with interactive functions, such as the NCurses library. You can check out my book Programmer’s Guide to NCurses, from Wiley Publishing, for more information.

check.png The end-of-file marker is represented by the EOF constant, defined in the stdio.h header file.

check.png The newline character is represented by the \n escape sequence.

check.png warning_bomb.eps The newline character's ASCII value may differ from machine to machine, so always specify the escape sequence \n for the newline.