- * Lexical style (e.g., naming conventions, indentation, placement of {}, comment style, etc.)
- * Standard macros that extend or alter the "base" language (e.g., use of an EQ macro for ==)
- * Program and function documentation, including the content and layout
- * Structure of header files
- * Main program organization
- * Error signaling and handling
- * Message format and specific standard messages
Facing a list of standards like this may be discouraging. How can you ever expect to cover all the areas, and how will you deal with all the existing code that doesn't follow the standards? The answer: one step at a time. If you haven't already developed a set of standards, start by creating a document, "C Programming Standards," with sections following the list above. Preferably, use a word processing package that lets you mark index entries and automatically produce a document index. The index will be an invaluable part of the printed standards as they grow.
Then start adding guidelines and examples. The following shows a sample entry:
Follow each ( and [ with a space, and precede each ) and ] with a space.
Example: x = myfunc( item_table[ i ] );
One of the major barriers to instituting programming standards is disagreement over what the "best" standard is. A group of programmers might agree on some broad principle, such as indenting conditional code to show its structure, but on specific guidelines such as the size of each level of indentation there often are many defensible points of view. To avoid "analysis paralysis," discuss the merits of each alternative and select one guideline as "good enough" to follow "most of the time." This may sound like a wishy-washy way to embark on standards, but it'll work fine if the programmers who develop and use the standards understand that, in most cases, following consistent guidelines across projects is more important than determining the absolute "best" rule. It's also important that programmers faithfully follow the standards, and that they don't view "most of the time" as an escape clause that lets them go their own way whenever they have a personal preference that's not consistent with the standards.
Sometimes, differing opinions about alternative guidelines may run so deep that even a "good enough" standard is hard to agree on. Consider a suggestion I made in Chapter 2, that macros EQ, LE, etc., be used instead of ==, <=, and the other relational symbols. The principle underlying this suggestion is to avoid the slippery errors caused by mistakenly using = (assignment) instead of == (equality). Unfortunately, this guideline conflicts with the principle that you should express program constructs in ways that are consistent with ways you express similar constructs in other contexts. The < and > symbols are widely used in other programming languages and general math discourse. The <=, and >= symbols are also widely used in other programming languages and are very similar to the and math symbols. So using LT, GT, LE, and GE instead of these symbols could be considered a step backward. Note that it's this same principle that C's use of == violates, since = is the commonly used symbol for equality in other programming languages and in math. Here we have a conflict that originates in a C flaw, and whose solution may introduce a different, if lesser, problem.
Reasonable arguments might be marshaled for either of three guidelines:
- 1. Use all of the standard C symbols
- 2. Use EQ instead of == (and possibly NE instead of <>), but use the standard C symbols for the rest of the operators
- 3. Use EQ, LE, etc., and don't use any of the standard C symbols
The first approach is risky and would only make sense if it were combined with some other guidelines, such as always checking a program with a compiler or "lint" utility that could detect the use of = where == was expected.
The second approach minimizes the amount of non-typical C code (it's safe to say most C programmers today use the standard C symbols) and eliminates the dangerous ==, but it introduces an inconsistency among the various relational operators. The third approach is easy to follow and is consistent, but it is not typical for either C programs or common math expressions.
If possible, one of the latter two guidelines should be adopted as "good enough." And, for this rule, followed all of the time because there are no potential conflicts with other lexical rules. If passions run deep, however, you might have to settle for a guideline that says: In any program use either Rule 2 or Rule 3 throughout the whole program. This lets a programmer use a reasonable alternative that he or she is comfortable with and maintains consistency at least at the program level. After a year or so of experience, you may be able to determine a strong preference for one or the other guidelines and be able to settle on a single "good enough" standard.
Permitting alternative coding styles costs a significant amount in consistency and reduces a programmer's ability to understand immediately the notation of a new program. Consequently, it's a tactic to use sparingly. But for some guidelines, it's the best way to move forward and at least reduce the number of variations in coding style.