Previous Table of Contents Next


Order in the Court

In Chapters 2 and 6, I pointed out some of the problems that arise from C’s rules (or lack of rules) for operator precedence and order of evaluation of expressions. I won’t go through all the rules or unusual results that can occur, but observe that an expression like

r = x * y * z;

may be evaluated as

tmp = x * y;
r   = tmp * z;

or as

tmp = y * z;
r   = x * tmp;

Note that even parentheses will not guarantee the ordering, and even (x * y) * z may be evaluated as

tmp = y * z;
r   = x * tmp;

In many cases, it may not matter what the order of evaluation is, but if it does, you should use separate statements to specify order-dependent operations.

The Name Game

As if C didn’t offer enough problems on its own, the C programming culture sometimes seems to strive to create more traps for the unwary. One example is the widely used “Hungarian” naming convention, which uses partial capitalization for identifiers. Because C is case-sensitive, a variable hDlg is different than the variable hdlg. Woe to the programmer who has identifiers that differ only in case. Not only is there the obvious potential for elusive errors caused by typing mistakes, but some link editors change all global symbols to uppercase when linking multiple files, causing both hDlg and hdlg to be treated as HDLG.

You won’t be able to avoid Hungarian notation when you work with some vendor-supplied libraries, such as the Microsoft Windows interface. But for your own code, especially global variables: Avoid identifiers that differ only in the case (i.e., upper and lower) of some letters. I recommend the simple, less error-prone, standard of using all lowercase identifiers, except for manifest constants. You should also be careful with some older link editors that may truncate global identifiers (the C standard requires only that the first 6 characters of an external identifier be used), causing the potential for additional collisions.

Although I’ve covered lots of C danger zones in the last 6 chapters, there are more waiting. Among the areas to watch carefully are: casting pointers; using C signals (Koenig has an enlightening — and alarming — discussion on using signals); using floating-point variables to approximate decimal values (such as currency); and portability problems, such as character representations and byte ordering. The books listed in Appendix A provide additional material on these topics. The principle that underlies all these rules is: Tread carefully in C; stick to simple and well-understood techiniques; and avoid “clever” programming. The truly clever C programmer is also an extremely cautious one.

C Coding Suggestions

*  Put parentheses (or other explicit delimiters) around the macro text and around each macro argument within the macro text.
*  Never pass an expression that has side effects as a macro argument.
*  Place simple increment and decrement operations in separate statements.
*  Avoid evaluating an argument more than once, if possible.
*  When defining a macro, be sure to consider all the contexts in which the macro may be used.
*  Carefully cast any operation that involves a char variable and any operand other than another char variable.
*  Always use int (not char) variables to store return values from fgetc, getc, getchar, putc, putchar, and ungetc functions.
*  Always declare a function prototype at the beginning of any file in which you use the function.
*  Define a header file with function prototypes for every file that has global functions that may be referenced in other files.
*  Declare formal parameters to functions as const, if they should not be changed.
*  Be sure you’ve coded parentheses after all function invocations.
*  Set errno to 0 before a function call, and use errno only after a function returns a value indicating the function failed.
*  In C, always use one pair of [] for each level of array subscripting.
*  Use separate statements to specify order-dependent operations.
*  Avoid identifiers that differ only in the case (i.e., upper and lower) of some letters.
*  Tread carefully in C; stick to simple and well-understood techiniques; and avoid “clever” programming.


Previous Table of Contents Next