Previous | Table of Contents | Next |
In our get_customer example, you might think the following alternative would be safe and still have a nice "C-food" flavor.
if (custid = get_customer() > 0) { /* Process the customer */ }
Now the code guards against negative, as well as zero, return values. Or does it? Something's fishy here. This code simply assigns 0 or 1 to custid because the > comparison operator has higher precedence (i.e., binds more tightly) than the assignment operator. This code is equivalent to
if (custid = (get_customer() > 0)) { /* Process the customer */ }
What you really need is:
if ((custid = get_customer()) > 0) { /* Process the customer */ }
C has 15 levels of operator precedence (so much for C being a "simple" language). Two other easy rules will keep you from floundering at C. Do all assignments as separate statements, not as a part of a more complex expression. And use parentheses liberally to explicitly define the order of evaluation.
Understand one thing about C, and all its mysteries are revealed. C was -- and is -- a language meant as a portable replacement for machine-dependent assembly languages. Keep this in mind when you consider the following example.
Suppose you code an array of part numbers and their names and a few lines to display a list of parts, as shown in Figure 2.1. If you remember C is for machine-level programming, you won't be suprised to find there's no part number 11. In C, 011 is not 11; it's 9! Integer constants that begin with 0 are octal (Get it? The 0 looks like O for Octal.)
Figure 2.1 Sample C Code
struct part { int part_number; char description[30]; } main() { int i; /* Array of part numbers and descriptions */ struct part part_table[100] = { {011, "Wrench" }, {067, "Screwdriver" }, {137, "Hammer" }, {260, "Pliers" }, /* etc. */ {0, "sentinel" } }; for (i=0; i<100; i++); /* Print the list of parts */ { if (part_table[i].part_number == 0) break; printf("%i %s", part_table[i].part_number, part_table[i].description); } }
There are even more subtle ways octal constants can sneak up on you. Suppose you want to read an integer part from the standard input and then output it, using
int part_number; scanf("%i", part_number); printf("Number %i", part_number);
If you enter 011, the program outputs 9. Unsurprisingly, the format %i specifies an integer input and output field. Surprisingly, if the input is 011, the value of part_number is 9. You see, on input, %i means "decimal, hexadecimal, or octal integer," whereas on output, %i means simply "decimal integer." Unless this is an application for PDP-11 system programmers, the code should be
int part_number; scanf("%d", part_number); printf("Number %d", part_number);
The format %d means "decimal integer" for both input and output. Unless your magic number is 8, you should use a lint utility or your editor to ferret out all %i format specifications and numbers that begin with 0.
The previous two examples actually have a much bigger problem than octal numbers. I miscoded the scanf function argument as part_number instead of &part_number. So instead of supplying scanf with the address where I want the input stored (i.e., the address of part_number), I supplied the uninitialized value of part_number. C is powerful, so powerful in fact, that scanf will trash some location in memory pointed to by whatever garbage is in part_number. If you're lucky, the trashed memory will be part of the debugger or operating system code, and you'll earn a C programming purple heart. To avoid winning too many battle ribbons, however, always double-check that you've supplied valid addresses for arguments to scanf and similar functions.
If you're new to C, you may think I'm blowing its problems out of proportion. You may wonder whether C's flaws significantly hamper the work-a-day C programmer. The answer is yes, most C programmers do suffer from C's flaws; but like some mainframe COBOL programmers and some midrange RPG programmers, C programmers sometimes take pride in their ability to overcome the language's deficiencies. And after enough years chasing errant pointers, many C programmers become numb to the pain of using a language that can crash the debugger and freeze their PC.
Conversations I have had with technical staff of several large microcomputer software companies illustrate what, I think, is the prevalent viewpoint in the C programming culture. I asked experienced C programmers whether they regularly encountered the kinds of problems I've described so far, and they all said, in effect, "Of course, it's just part of programming in C." Then I asked them how they handled a couple of the most common problems, and with one exception, they said they relied on "careful programming," lint filters, and good debuggers. The one exception said he built his own layer of abstract data types to insulate himself from C. (In following chapters, I offer techniques along these lines.)
The most insightful reflection about C I've heard is from the computer scientist Bertrand Meyer, who designed the Eiffel programming language. He said, "How could I even try to teach systematic algorithm construction when I knew the bulk of the C students' time was spent fighting tricky pointer arithmetic, chasing memory allocation bugs, trying to figure out whether an argument was a value or a pointer, making sure the number of asterisks was right, and so on. I'm afraid it will be hard to recover from the damage caused by C to an entire generation of programmers."
The same assessment was put more briefly by the programmer who said, "C's a double-edged swordwithout any handle."
C can do you harm, and not just if you're inexperienced. In this book, I will try to give you a handle on C so you can wield it as safely as possible. In future chapters, I will describe other ways that C can trip you up and suggest programming practices to avoid common problems. If you're considering C, either for workstation or AS/400 development, you'll gain a better understanding of some of the risks you face. If you're already using C, these tips will help you minimize your risks.
* * * * *
To experienced C programmers only: Did you catch the other errors I made in coding Figure 2.1? I'll point them out in Chapter 3.
Previous | Table of Contents | Next |