``C Code. C code run. Run, code, run... PLEASE!!!'' - Barbara Tongue
If you use
enum
s,
the first enum constant should have a non-zero value,
or the first constant should indicate an error.
enum { STATE_ERR, STATE_START, STATE_NORMAL, STATE_END } state_t;
enum { VAL_NEW=1, VAL_NORMAL, VAL_DYING, VAL_DEAD } value_t;
Check for error return values, even from functions that ``can't''
fail.
Consider that
close()
and
fclose()
can and do fail, even when all prior file operations have succeeded.
Write your own functions so that they test for errors
and return error values or abort the program in a well-defined way.
Include a lot of debugging and error-checking code
and leave most of it in the finished product.
Check even for ``impossible'' errors. [8]
Use the
assert
facility to insist that
each function is being passed well-defined values,
and that intermediate results are well-formed.
Build in the debug code using as few #ifdefs as possible.
For instance, if
``mm_malloc
''
is a debugging memory allocator, then
MALLOC
will select the appropriate allocator,
avoids littering the code with #ifdefs,
and makes clear the difference between allocation calls being debugged
and extra memory that is allocated only during debugging.
#ifdef DEBUG
# define MALLOC(size) (mm_malloc(size))
#else
# define MALLOC(size) (malloc(size))
#endif
Check bounds even on things that ``can't'' overflow.
A function that writes on to variable-sized storage
should take an argument
maxsize
that is the size of the destination.
If there are times when the size of the destination is unknown,
some `magic' value of
maxsize
should mean ``no bounds checks''.
When bound checks fail,
make sure that the function does something useful
such as abort or return an error status.
/*
* INPUT: A null-terminated source string `src' to copy from and
* a `dest' string to copy to. `maxsize' is the size of `dest'
* or UINT_MAX if the size is not known. `src' and `dest' must
* both be shorter than UINT_MAX, and `src' must be no longer than
* `dest'.
* OUTPUT: The address of `dest' or NULL if the copy fails.
* `dest' is modified even when the copy fails.
*/
char *
copy (char *dest, size_t maxsize, char *src)
{
char *dp = dest;
while (maxsize-- > 0)
if ((*dp++ = *src++) == '\0')
return (dest);
return (NULL);
}
In all, remember that a program that produces wrong answers twice as fast is infinitely slower. The same is true of programs that crash occasionally or clobber valid data.