Файл: Recommended C Style and Coding Standards (L.W. Cannon, 1990).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 390

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

- 6 -

Such usage should be commented to make it clear that another file’s variables are being used; the comment should name the other file. If your debugger hides static objects you need to see during debugging, declare them as STATIC and #define STATIC as needed.

The most important types should be highlighted by typedeffing them, even if they are only integers, as the unique name makes the program easier to read (as long as there are only a few things typedeffed to integers!). Structures may be typedeffed when they are declared. Give the struct and the typedef the same name.

typedef struct splodge_t { int sp_count; char*sp_name, *sp_alias;

} splodge_t;

The return type of functions should always be declared. If function prototypes are available, use them. One common mistake is to omit the declaration of external math functions that return double. The compiler then assumes that the return value is an integer and the bits are dutifully converted into a (meaningless) floating point value.

‘‘C takes the point of view that the programmer is always right.’’ — Michael DeCorte

5. Function Declarations

Each function should be preceded by a block comment prologue that gives a short description of what the function does and (if not clear) how to use it. Discussion of non-trivial design decisions and side-effects is also appropriate. Avoid duplicating information clear from the code.

The function return type should be alone on a line, (optionally) indented one stop4. Do not default to int; if the function does not return a value then it should be given return type void5. If the value returned requires a long explanation, it should be given in the prologue; otherwise it can be on the same line as the return type, tabbed over. The function name (and the formal parameter list) should be alone on a line, in column 1. Destination (return value) parameters should generally be first (on the left). All formal parameter declarations, local declarations and code within the function body should be tabbed over one stop. The opening brace of the function body should be alone on a line beginning in column 1.

Each parameter should be declared (do not default to int). In general the role of each variable in the function should be described. This may either be done in the function comment or, if each declaration is on its own line, in a comment on that line. Loop counters called ‘‘i’’, string pointers called ‘‘s’’, and integral types called ‘‘c’’ and used for characters are typically excluded. If a group of functions all have a like parameter or local variable, it helps to call the repeated variable by the same name in all functions. (Conversely, avoid using the same name for different purposes in related functions.) Like parameters should also appear in the same place in the various argument lists.

Comments for parameters and local variables should be tabbed so that they line up underneath each other. Local variable declarations should be separated from the function’s statements by a blank line.

Be careful when you use or declare functions that take a variable number of arguments (‘‘varargs’’). There is no truly portable way to do varargs in C. Better to design an interface that uses a fixed number of arguments. If you must have varargs, use the library macros for declaring functions with variant argument lists.

If the function uses any external variables (or functions) that are not declared globally in the file, these should have their own declarations in the function body using the extern keyword.

Avoid local declarations that override declarations at higher levels. In particular, local variables should not be redeclared in nested blocks. Although this is valid C, the potential confusion is enough that

lint will complain about it when given the −h option. hhhhhhhhhhhhhhhhhh

4.‘‘Tabstops’’ can be blanks (spaces) inserted by your editor in clumps of 2, 4, or 8. Use actual tabs where possible.

5.#define void or #define void int for compilers without the void keyword.

Recommended C Coding Standards

Revision: 6.0

25 June 1990


- 7 -

6. Whitespace

int i;main(){for(;i["]<i;++i){--i;}"];read(’-’-’-’,i+++"hell\ o, world!\n",’/’/’/’));}read(j,i,p){write(j/p+p,i---j,i/i);}

— Dishonorable mention, Obfuscated C Code Contest, 1984. Author requested anonymity.

Use vertical and horizontal whitespace generously. Indentation and spacing should reflect the block structure of the code; e.g., there should be at least 2 blank lines between the end of one function and the comments for the next.

A long string of conditional operators should be split onto separate lines.

if (foo->next= =NULL && totalcount<needed && needed<=MAX_ALLOT

&& server_active(current_input)) { ...

Might be better as

if (foo->next = = NULL

&&totalcount < needed && needed <= MAX_ALLOT

&&server_active(current_input))

{

...

Similarly, elaborate for loops should be split onto different lines.

for (curr = *listp, trail = listp; curr != NULL;

trail = &(curr->next), curr = curr->next )

{

...

Other complex expressions, particularly those using the ternary ? : operator, are best split on to several lines, too.

c = (a == b)

? d + f(a) : f(b) - d;

Keywords that are followed by expressions in parentheses should be separated from the left parenthesis by a blank. (The sizeof operator is an exception.) Blanks should also appear after commas in argument lists to help separate the arguments visually. On the other hand, macro definitions with arguments must not have a blank between the name and the left parenthesis, otherwise the C preprocessor will not recognize the argument list.

7. Examples

Recommended C Coding Standards

Revision: 6.0

25 June 1990

- 8 -

/*

*Determine if the sky is blue by checking that it isn’t night.

*CAVEAT: Only sometimes right. May return TRUE when the answer

*is FALSE. Consider clouds, eclipses, short days.

*NOTE: Uses ‘hour’ from ‘hightime.c’. Returns ‘int’ for

*compatibility with the old version.

*/

int

/* true or false */

skyblue()

{

extern int hour;

/* current hour of the day */

return (hour >= MORNING && hour <= EVENING);

}

/*

*Find the last element in the linked list

*pointed to by nodep and return a pointer to it.

*Return NULL if there is no last element.

*/

node_t *

tail(nodep)

node_t *nodep;

/* pointer to head of list */

{

register node_t

*np;

/* advances to NULL */

register node_t

*lp;

/* follows one behind np */

if (nodep = = NULL) return (NULL);

for (np = lp = nodep; np != NULL; lp = np, np = np->next) ; /* VOID */

return (lp);

}

8. Simple Statements

There should be only one statement per line unless the statements are very closely related.

case FOO:

oogle (zork);

boogle (zork);

break;

case

BAR:

oogle

(bork);

boogle

(zork);

break;

case

BAZ:

oogle

(gork);

boogle

(bork);

break;

The null body of a for or while loop should be alone on a line and commented so that it is clear that the null body is intentional and not missing code.

while (*dest++ = *src++)

;/* VOID */

Do not default the test for non-zero, i.e.

if (f( ) != FAIL)

is better than

if (f( ))

even though FAIL may have the value 0 which C considers to be false. An explicit test will help you out later when somebody decides that a failure return should be −1 instead of 0. Explicit comparison should be

Recommended C Coding Standards

Revision: 6.0

25 June 1990


- 9 -

used even if the comparison value will never change; e.g., ‘‘if (!(bufsize % sizeof(int)))’’ should be written instead as ‘‘if ((bufsize % sizeof(int)) = = 0)’’ to reflect the numeric (not boolean) nature of the test. A frequent trouble spot is using strcmp to test for string equality, where the result should never ever be defaulted. The preferred approach is to define a macro STREQ.

#define STREQ(a, b) (strcmp((a), (b)) = = 0)

The non-zero test is often defaulted for predicates and other functions or expressions which meet the following restrictions:

gEvaluates to 0 for false, nothing else.

gIs named so that the meaning of (say) a ‘true’ return is absolutely obvious. Call a predicate isvalid or valid, not checkvalid.

It is common practice to declare a boolean type ‘‘bool’’ in a global include file. The special names improve readability immensely.

typedef int bool; #define FALSE 0 #define TRUE 1

or

typedef enum { NO=0, YES } bool;

Even with these declarations, do not check a boolean value for equality with 1 (TRUE, YES, etc.); instead test for inequality with 0 (FALSE, NO, etc.). Most functions are guaranteed to return 0 if false, but only non-zero if true. Thus,

if (func() = = TRUE) { ...

must be written

if (func() != FALSE) { ...

It is even better (where possible) to rename the function/variable or rewrite the expression so that the meaning is obvious without a comparison to true or false (e.g., rename to isvalid()).

There is a time and a place for embedded assignment statements. In some constructs there is no better way to accomplish the results without making the code bulkier and less readable.

while ((c = getchar()) != EOF) { process the character

}

The ++ and − − operators count as assignment statements. So, for many purposes, do functions with side effects. Using embedded assignment statements to improve run-time performance is also possible. However, one should consider the tradeoff between increased speed and decreased maintainability that results when embedded assignments are used in artificial places. For example,

a = b + c; d = a + r;

should not be replaced by

d = (a = b + c) + r;

even though the latter may save one cycle. In the long run the time difference between the two will decrease as the optimizer gains maturity, while the difference in ease of maintenance will increase as the human memory of what’s going on in the latter piece of code begins to fade.

Goto statements should be used sparingly, as in any well-structured code. The main place where they can be usefully employed is to break out of several levels of switch, for, and while nesting, although the need to do such a thing may indicate that the inner constructs should be broken out into a separate func-

Recommended C Coding Standards

Revision: 6.0

25 June 1990


- 10 -

tion, with a success/failure return code.

for (...) {

while (...) {

...

if (disaster) goto error;

}

}

...

error:

clean up the mess

When a goto is necessary the accompanying label should be alone on a line and tabbed one stop to the left of the code that follows. The goto should be commented (possibly in the block header) as to its utility and purpose. Continue should be used sparingly and near the top of the loop. Break is less troublesome.

Parameters to non-prototyped functions sometimes need to be promoted explicitly. If, for example, a function expects a 32-bit long and gets handed a 16-bit int instead, the stack can get misaligned. Problems occur with pointer, integral, and floating-point values.

9. Compound Statements

A compound statement is a list of statements enclosed by braces. There are many common ways of formatting the braces. Be consistent with your local standard, if you have one, or pick one and use it consistently. When editing someone else’s code, always use the style used in that code.

control { statement; statement;

}

The style above is called ‘‘K &R style’’, and is preferred if you haven’t already got a favorite. With K&R style, the else part of an if-else statement and the while part of a do-while statement should appear on the same line as the close brace. With most other styles, the braces are always alone on a line.

When a block of code has several labels (unless there are a lot of them), the labels are placed on separate lines. The fall-through feature of the C switch statement, (that is, when there is no break between a code segment and the next case statement) must be commented for future maintenance. A lint-style comment/directive is best.

switch (expr) { case ABC:

case DEF: statement; break;

case UVW: statement; /*FALLTHROUGH*/

case XYZ: statement; break;

}

Here, the last break is unnecessary, but is required because it prevents a fall-through error if another case is added later after the last one. The default case, if used, should be last and does not require a break if it is last.

Recommended C Coding Standards

Revision: 6.0

25 June 1990

- 11 -

Whenever an if-else statement has a compound statement for either the if or else section, the statements of both the if and else sections should both be enclosed in braces (called fully bracketed syntax).

if (expr) { statement;

} else { statement; statement;

}

Braces are also essential in if-if-else sequences with no second else such as the following, which will be parsed incorrectly if the brace after (ex1) and its mate are omitted:

if (ex1) {

if (ex2) { funca();

}

} else { funcb();

}

An if-else with else if should be written with the else conditions left-justified.

if (STREQ (reply, "yes")) { statements for yes

...

}else if (STREQ (reply, "no")) {

...

}else if (STREQ (reply, "maybe")) {

...

}else {

statements for default

...

}

The format then looks like a generalized switch statement and the tabbing reflects the switch between exactly one of several alternatives rather than a nesting of statements.

Do-while loops should always have braces around the body.

The following code is very dangerous:

#ifdef CIRCUIT

#define CLOSE_CIRCUIT(circno) { close_circ(circno); } #else

#define CLOSE_CIRCUIT(circno)

#endif

...

if (expr) statement;

else

CLOSE_CIRCUIT(x)

++i;

Note that on systems where CIRCUIT is not defined the statement ‘‘++i;’’ will only get executed when expr is false! This example points out both the value of naming macros with CAPS and of making code fully-bracketed.

Recommended C Coding Standards

Revision: 6.0

25 June 1990


- 12 -

Sometimes an if causes an unconditional control transfer via break, continue, goto, or return. The else should be implicit and the code should not be indented.

if (level > limit) return (OVERFLOW)

normal(); return (level);

The ‘‘flattened’’ indentation tells the reader that the boolean test is invariant over the rest of the enclosing block.

10. Operators

Unary operators should not be separated from their single operand. Generally, all binary operators except ‘.’ and ‘−>’ should be separated from their operands by blanks. Some judgement is called for in the case of complex expressions, which may be clearer if the ‘‘inner’’ operators are not surrounded by spaces and the ‘‘outer’’ ones are.

If you think an expression will be hard to read, consider breaking it across lines. Splitting at the lowest-precedence operator near the break is best. Since C has some unexpected precedence rules, expressions involving mixed operators should be parenthesized. Too many parentheses, however, can make a line harder to read because humans aren’t good at parenthesis-matching.

There is a time and place for the binary comma operator, but generally it should be avoided. The comma operator is most useful to provide multiple initializations or operations, as in for statements. Complex expressions, for instance those with nested ternary ? : operators, can be confusing and should be avoided if possible. There are some macros like getchar where both the ternary operator and comma operators are useful. The logical expression operand before the ? : should be parenthesized and both return values must be the same type.

11. Naming Conventions

Individual projects will no doubt have their own naming conventions. There are some general rules however.

gNames with leading and trailing underscores are reserved for system purposes and should not be used for any user-created names. Most systems use them for names that the user should not have to know. If you must have your own private identifiers, begin them with a letter or two identifying the package to which they belong.

g#define constants should be in all CAPS.

gEnum constants are Capitalized or in all CAPS

gFunction, typedef, and variable names, as well as struct, union, and enum tag names should be in lower case.

gMany macro ‘‘functions’’ are in all CAPS. Some macros (such as getchar and putchar) are in lower case since they may also exist as functions. Lower-case macro names are only acceptable if the macros behave like a function call, that is, they evaluate their parameters exactly once and do not assign values to named parameters. Sometimes it is impossible to write a macro that behaves like a function even though the arguments are evaluated exactly once.

gAvoid names that differ only in case, like foo and Foo. Similarly, avoid foobar and foo_bar. The potential for confusion is considerable.

gSimilarly, avoid names that look like each other. On many terminals and printers, ‘l’, ‘1’ and ‘I’ look quite similar. A variable named ‘l’ is particularly bad because it looks so much like the constant ‘1’.

In general, global names (including enums) should have a common prefix identifying the module that they belong with. Globals may alternatively be grouped in a global structure. Typedeffed names often have ‘‘_t’’ appended to their name.

Recommended C Coding Standards

Revision: 6.0

25 June 1990