Chapter 26
Ten Common Boo-Boos
In This Chapter
Conditional foul-ups
== v. =
Dangerous loop semicolons
Commas in for
loops
Missing break in a switch structure
Missing parentheses and curly brackets
Pay heed to that warning
Endless loops
scanf()
blunders
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))
== 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.
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.
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.
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
.
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.
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.
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.
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.
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.
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.
The end-of-file marker is represented by the EOF constant, defined in the stdio.h
header file.
The newline character is represented by the \n
escape sequence.
The newline character's ASCII value may differ from machine to machine, so always specify the escape sequence \n
for the newline.