Файл: Recommended C Style and Coding Standards (L.W. Cannon, 1990).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 394
Скачиваний: 2
- 19 -
two separate fields are never concatenated and treated as a unit. [1,3] Actually, it is nonportable to concatenate any two variables.
gThere may be unused holes in structures. Suspect unions used for type cheating. Specifically, a value should not be stored as one type and retrieved as another. An explicit tag field for unions may be useful.
gDifferent compilers use different conventions for returning structures. This causes a problem when libraries return structure values to code compiled with a different compiler. Structure pointers are not a problem.
gDo not make assumptions about the parameter passing mechanism. especially pointer sizes and parameter evaluation order, size, etc. The following code, for instance, is very nonportable.
c = foo (getchar(), getchar());
char |
|
foo (c1, c2, c3) |
|
char c1, c2, c3; |
|
{ |
|
char bar = *(&c1 + 1); |
|
return (bar); |
/* often won’t return c2 */ |
} |
This example has lots of problems. The stack may grow up or down (indeed, there need not even be a stack!). Parameters may be widened when they are passed, so a char might be passed as an int, for instance. Arguments may be pushed left-to-right, right-to-left, in arbitrary order, or passed in registers (not pushed at all). The order of evaluation may differ from the order in which they are pushed. One compiler may use several (incompatible) calling conventions.
gOn some machines, the null character pointer ((char *)0) is treated the same way as a pointer to a null string. Do not depend on this.
gDo not modify string constants7. One particularly notorious (bad) example is
s = "/dev/tty??";
strcpy (&s[8], ttychars);
gThe address space may have holes. Simply computing the address of an unallocated element in an array (before or after the actual storage of the array) may crash the program. If the address is used in a comparison, sometimes the program will run but clobber data, give wrong answers, or loop forever. In ANSI C, a pointer into an array of objects may legally point to the first element after the end of the array; this is usually safe in older implementations. This ‘‘outside’’ pointer may not be dereferenced.
gOnly the = = and ! = comparisons are defined for all pointers of a given type. It is only portable to use <, <=, >, or >= to compare pointers when they both point in to (or to the first element after) the same array. It is likewise only portable to use arithmetic operators on pointers that both point into the same array or the first element afterwards.
gWord size also affects shifts and masks. The following code will clear only the three rightmost bits of an int on some 68000s. On other machines it will also clear the upper two bytes.
x&= 0177770
Use instead
x &= ˜07
hhhhhhhhhhhhhhhhhh
7.Some libraries attempt to modify and then restore read-only string variables. Programs sometimes won’t port because of these broken libraries. The libraries are getting better.
Recommended C Coding Standards |
Revision: 6.0 |
25 June 1990 |
- 20 -
which works properly on all machines. Bitfields do not have these problems.
gSide effects within expressions can result in code whose semantics are compiler-dependent, since C’s order of evaluation is explicitly undefined in most places. Notorious examples include the following.
a[i] = b[i++];
In the above example, we know only that the subscript into b has not been incremented. The index into a could be the value of i either before or after the increment.
struct bar_t { struct bar_t *next; } bar; bar->next = bar = tmp;
In the second example, the address of ‘‘bar->next’’ may be computed before the value is assigned to ‘‘bar’’.
bar = bar->next = tmp;
In the third example, bar can be assigned before bar->next. Although this appears to violate the rule that ‘‘assignment proceeds right-to-left’’, it is a legal interpretation. Consider the following example:
long i; short a[N]; i = old
i = a[i] = new;
The value that ‘‘i’’ is assigned must be a value that is typed as if assignment proceeded right-to-left. However, ‘‘i’’ may be assigned the value ‘‘(long)(short)new’’ before ‘‘a[i]’’ is assigned to. Compilers do differ.
gBe suspicious of numeric values appearing in the code (‘‘magic numbers’’).
gAvoid preprocessor tricks. Tricks such as using /**/ for token pasting and macros that rely on argument string expansion will break reliably.
#define FOO(string) (printf("string = %s",(string)))
...
FOO(filename);
Will only sometimes be expanded to
(printf("filename = %s",(filename)))
Be aware, however, that tricky preprocessors may cause macros to break accidentally on some machines. Consider the following two versions of a macro.
#define |
LOOKUP(chr) |
(a[’c’+(chr)]) |
/* |
Works as intended. */ |
#define |
LOOKUP(c) |
(a[’c’+(c)]) |
/* |
Sometimes breaks. */ |
The second version of LOOKUP can be expanded in two different ways and will cause code to break mysteriously.
gBecome familiar with existing library functions and defines. (But not too familiar. The internal details of library facilities, as opposed to their external interfaces, are subject to change without warning. They are also often quite unportable.) You should not be writing your own string compare routine, terminal control routines, or making your own defines for system structures. ‘‘Rolling your own’’ wastes your time and makes your code less readable, because another reader has to figure out whether you’re doing something special in that reimplemented stuff to justify its existence. It also prevents your program from taking advantage of any microcode assists or other means of improving performance of system routines. Furthermore, it’s a fruitful source of bugs. If possible, be aware of the differences between the common libraries (such as ANSI, POSIX, and so on).
Recommended C Coding Standards |
Revision: 6.0 |
25 June 1990 |
- 21 -
gUse lint when it is available. It is a valuable tool for finding machine-dependent constructs as well as other inconsistencies or program bugs that pass the compiler. If your compiler has switches to turn on warnings, use them.
gSuspect labels inside blocks with the associated switch or goto outside the block.
gWherever the type is in doubt, parameters should be cast to the appropriate type. Always cast NULL when it appears in non-prototyped function calls. Do not use function calls as a place to do type cheating. C has confusing promotion rules, so be careful. For example, if a function expects a 32-bit long and it is passed a 16-bit int the stack can get misaligned, the value can get promoted wrong, etc.
gUse explicit casts when doing arithmetic that mixes signed and unsigned values.
gThe inter-procedural goto, longjmp, should be used with caution. Many implementations ‘‘forget’’ to restore values in registers. Declare critical values as volatile if you can or comment them as
VOLATILE.
gSome linkers convert names to lower-case and some only recognize the first six letters as unique. Programs may break quietly on these systems.
gBeware of compiler extensions. If used, document and consider them as machine dependencies.
gA program cannot generally execute code in the data segment or write into the code segment. Even when it can, there is no guarantee that it can do so reliably.
17.ANSI C
Modern C compilers support some or all of the ANSI proposed standard C. Whenever possible, write code to run under standard C, and use features such as function prototypes, constant storage, and volatile storage. Standard C improves program performance by giving better information to optimizers. Standard C improves portability by insuring that all compilers accept the same input language and by providing mechanisms that try to hide machine dependencies or emit warnings about code that may be machinedependent.
17.1. Compatibility
Write code that is easy to port to older compilers. For instance, conditionally #define new (standard) keywords such as const and volatile in a global .h file. Standard compilers pre-define the preprocessor symbol _ _STDC_ _8. The void* type is hard to get right simply, since some older compilers understand void but not void*. It is easiest to create a new (machineand compiler-dependent) VOIDP type, usually char* on older compilers.
hhhhhhhhhhhhhhhhhh
8. |
Some compilers predefine _ _STDC_ _ to be 0, in an attempt to indicate partial compliance with the ANSI C standard. |
Unfortunately, it is not possible to determine which ANSI facilities are provided. Thus, such compilers are broken. See the |
|
rule about ‘‘don’t write around a broken compiler unless you are forced to.’’ |
Recommended C Coding Standards |
Revision: 6.0 |
25 June 1990 |
- 22 -
#if _ _STDC_ _
typedef void *voidp;
#define COMPILER_SELECTED #endif
#ifdef A_TARGET
#define const
#define volatile
#define void int typedef char *voidp;
#define COMPILER_SELECTED #endif
#ifdef ...
...
#endif
#ifdef COMPILER_SELECTED
#undef COMPILER_SELECTED #else
{NO TARGET SELECTED! }
#endif
Note that under ANSI C, the ‘#’ for a preprocessor directive must be the first non-whitespace character on a line. Under older compilers it must be the first character on the line.
When a static function has a forward declaration, the forward declaration must include the storage class. For older compilers, the class must be ‘‘extern’’. For ANSI compilers, the class must be ‘‘static’’. but global functions must still be declared as ‘‘extern’’. Thus, forward declarations of static functions should use a #define such as FWD_STATIC that is #ifdeffed as appropriate.
An ‘‘#ifdef NAME’’ should end with either ‘‘#endif’’ or ‘‘#endif /* NAME */’’, not with ‘‘#endif NAME’’. The comment should not be used on short #ifdefs, as it is clear from the code.
ANSI trigraphs may cause programs with strings containing ‘‘??’’ may break mysteriously.
17.2. Formatting
The style for ANSI C is the same as for regular C, with two notable exceptions: storage qualifiers and parameter lists.
Because const and volatile have strange binding rules, each const or volatile object should have a separate declaration.
int |
const |
*s; |
/* |
YES */ |
|
int |
const |
*s, |
*t; |
/* |
NO */ |
Prototyped functions merge parameter declaration and definition in to one list. Parameters should be commented in the function comment.
/*
*‘bp’: boat trying to get in.
*‘stall’: a list of stalls, never NULL.
*returns stall number, 0 => no room.
*/
int
enter_pier (boat_t const *bp, stall_t *stall)
{
...
Recommended C Coding Standards |
Revision: 6.0 |
25 June 1990 |