**Rule 1-1 (****Section 1.1): **Any macro definition containing operators needs parentheses around the entire definition. Each appearance of a macro argument in the definition also needs to be parenthesized if an embedded operator in the argument could cause a precedence problem.

**Rule 1-2 (Section 1.1):** Reliable modification of defined constants requires an environmental capability: there must be a means for ensuring that all files comprising a program have been compiled using the same set of headers. The UNIX make command is one such capability.)

**Rule 1-3 (Section 1.1): **If there are limitations on the modifiability of a defined constant, indicate the limitations with a comment:

#define EOF (-1) /* DO NOT M0DIFY: ctype.h expects -1 value */

**Rule 1-4 (Section 1.1): **If one definition affects another, embody the relationship in the definition; do not give two independent definitions.

**Rule 1-5 (Section 1.1): **If a value is given for a #defined name, do not defeat its modifiability by assuming its value in expressions.

**Rule 1-6 (Section 1.1): **Use limits.h for environment-dependent values.

**Rule 1-7 (Section 1.2): **Use a consistent set of project-wide defined types.

**Rule 1-8 (Section 1.3): **Be sure that all functions are declared before use; headers are the most reliable way.

**Rule 1-9 (Section 1.3): **Create a project-wide "local" header for standard definitions and inclusions.

**Rule 1-10 (Section 1.4): **Use UPPERCASE names for unsafe macro functions, to emphasize the restrictions on their usage.

**Rule 1-11 (Section 1.4): **Never invoke an unsafe macro with arguments containing assignment, increment/decrement, or function call.

**Rule 1-12 (Section 1.4): **Whenever possible, use safe macro functions.

**Rule 1-13 (Section 1.6): **Use #if 0 if there is a need to comment-out sections of code.

**Rule 1-14 (Section 1.6): **Enclose each header in an "inclusion sandwich."

**Rule 2-1 (Section 2.1): **When exactness counts in converting floating-point to integer, be sure the value being converted is non-negative.

**Rule 2-2 (Section 2.1): **Test errno before using results from the math functions.

**Rule 2-3 (Section 2.3): **Use the <ctype.h> facilities for character tests and upper-lower conversions.

**Rule 2-4 (Section 2.4): **Make sure that Boolean variables are assigned the values zero and one. This means that the type tbool is always adequate, and if this rule is part of local standards, the types bool and tbool could be made synonymous.

**Rule 2-5 (Section 2.4): **Make sure that each test condition is Boolean, involving only Boolean types or relational and logical operators.

**Rule 2-6 (Section 2.5): **An enumeration's constants should all be initialized, or else none of them should be initialized.

**Rule 2-7 (Section 2.5): **Write programs as if enumeration variables could receive no values other than the associated enumeration constants. Treat the enumeration types as if they were unique types, not for any arithmetic int usages. Convert between enumeration variables and integer values only by use of an explicit cast.

**Rule 2-8 (Section 2.6): **Include "one-too-far" values in the ranges for variables, if they are needed for loop terminations or other testing purposes.

**Rule 2-9 (Section 2.6): **Function parameters accepting the size of an arbitrarily large object should be declared with size_t type.

**Rule 2-10 (Section 2.7): **When two unsigned int's are subtracted, convert the result using either (unsigned) or UI_TO_I.

**Rule 2-11 (Section 2.7):** Use IMOD (or some similar mechanism) to ensure that a non-negative modulo result is produced.

**Rule 2-12 (Section 2.8):** In (signed) integer arithmetic, assume that

overflow is illegal, may be detected (hence should never be programmed), and cannot be trapped or ignored.

**Rule 2-13 (Section 2.9):** Document the defining properties of declared names in a comment on the declaration, using a convention such as "colon followed by property."

**Rule 3-1 (Section 3.1):** Storage class (if any) should precede the type specifier.

**Rule 3-2 (Section 3.1):** If a variable has an initialization, its declaration should have a source line to itself.

**Rule 3-3 (Section 3.1):** Document the defining property of a data object with a comment on its declaration. Ensure that this defining property remains invariant (unchanging) as much as possible throughout the computation, and document any exceptions.

**Rule 3-4 (Section 3.1):** A program is easier to write correctly and to understand if all arrays are made complete before the array is used.

**Rule 3-5 (Section 3.1):** If an array's defining property can be true even if not all elements are defined, indicate the property on the array's declaration. For example,

char s[10]; /*: string */

**Rule 3-6 (Section 3.2):** Use executable assertions whenever they are simpler than the code being protected, and when the time to execute the assertions is not much greater than the time required to execute the code.

**Rule 4-l (Section 4.1):** In each pointer assignment, the right-hand-side value must have exactly the same ("converted") pointer type as the left-hand-side.

**Rule 4-2 (Section 4.2): **The default requirement for pointer parameters is that they must point to storage that is entirely defined. Whenever a pointer parameter can accept something else, this should be explicitly stated on that parameter's declaration comment.

**Rule 4-3 (Section 4.3):** A function in which the address of an automatic variable is assigned to a non-automatic pointer must contain a comment to that effect. In any function with such a comment, each return from the function is an event requiring verification that no dangling pointers are left.

**Rule 6-1 (Section 6.1): **In portable programming, do not hard-code the numeric values of structure offsets. The values may be different in each environment. Refer to members by their symbolic member names only.

**Rule 6-2 (Section 6.2): **Names with leading underscore should only appear in code that is privy to the internal details of the associated data structure, not in "user-level" portable code.

**Rule 6-3 (Section 6.2): **Use the "leading underscore" name format for tag and member names if the internal details of the structure are not to be inspected by functions outside of the package. Conversely, avoid leading underscores if the details of the structure are available for inspection by functions that use the structure.

**Rule 6-4 (Section 6.3): **If a structure is not well-defined when initialized to zero, document this fact in a comment. (The program will in general be simpler if the members are defined such that the zero-initialized structure is well-defined.)

**Rule 6-5 (Section 6.5): **In portable code, do not depend upon the allocation order of bit-fields within a word.

**Rule 6-6 (Section 6.7): **Regarding parameters which are pointers to structures, an "out" pointer parameter is assumed to be non-NULL, pointing to the storage for a structure of the specified type. "In" and "in-out" pointer parameters are assumed to point to a well-defined structure of the specified type. Any exceptions should be noted in a comment on the parameter declaration.

**Rule 7-1 (Section 7.1): **When a pointer p is passed to the free function, the programmer must determine how many pointers are pointing into the freed storage. (This number is known as the "reference count" of the storage.) Steps must be taken (such as assigning NULL) to ensure that none of these pointers are subsequently used to access the freed storage.

**Rule 7-2 (Section 7.1): **For every instance in which a program allocates storage, there should be an easily identifiable instance in which that storage is later freed.

**Rule 8-1 (Section 8.2): **Always test the returned value from fopen to be sure that the open succeeded.

The headers shown in this book show function parameters in comments associated with each function declaration, in this style:

double atof(); /* PARMS(char *s) */

In this form, the comments serve purely for documentation purposes. However, the ANSI C draft envisions a style of function declaration called a "prototype," in which the function parameters are actually stated in the declaration, like this:

double atof(char *s);

When the compiler has seen a prototype declaration, it can then perform type-checking of the arguments, much as the lint checker does. More than this, prototypes can also cause the conversion of function arguments. If, for example, the header <math.h> declares sqrt as

double sqrt(double x);

int i = 10;

double y;

y = sqrt(i);

will cause the conversion of the integer i to the expected double parameter type.

void reverse PARMS((char *s));

#define PARMS(x) () /* empty parens */

The declaration of reverse as given above would produce

void reverse ();

The definition of PARMS for newer compilers would simply produce the argument:

#define PARMS(x) x

Thus the declaration of reverse would produce

void reverse (char *s);

#define NOSIDE(expr) $noside(expr)

Then, the definition of unsafe macros could be given in this form:

#define ABS(x) (NOSIDE(x) < 0 ? -(x) : (x))

The compiler would then give a diagnostic if x contained side-effects.

#define NOSIDE(x) (x)

The output file shown as sqrtx.out was produced by the White-smiths PDP-11/23 compiler (version 2.3). Similar small round-off errors are produced by most other math libraries. Here is a similar output from the Lattice 8088 compiler (version 2.15), which uses the 8087 auxiliary FPP chip:

sqrtx86.out:

2.0 2.00000000000000044 -4.44E-16

3.0 2.99999999999999955 4.44E-16

5.0 5.00000000000000088 -8.88E-16

6.0 5.99999999999999911 8.88E-16

7.0 7.00000000000000088 -8.88E-16

8.0 8.00000000000000177 -1.78E-15

10.0 10.00000000000000176 -1.78E-15

12.0 11.99999999999999821 1.78E-15

13.0 12.99999999999999822 1.78E-15

15.0 15.00000000000000176 -1.78E-15

18.0 17.99999999999999642 3.55E-15

19.0 19.00000000000000355 -3.55E-15

20.0 20.00000000000000353 -3.55E-15

23.0 22.99999999999999642 3.55E-15

24.0 23.99999999999999642 3.55E-15

As of June 1985, draft ANSI C requires that floating-to-integer truncation must be toward zero, whether the floating value is positive or negative. Since earlier C compilers may differ in the treatment of the rounding of negative floating values, the conservative course for portability is still to convert only positive values.

The matherr function has not been adopted by ANSI C, so its use is not generally portable.

These are the math functions that are declared in <math.h> in recent C libraries:

double acos(x) - arc cosine of x

double asin(x) - arc sine of x

double atan(x) - arc tangent of x

double atan2(y, x) - arc tangent of y/x

double ceil(x) - smallest integer not less than x

double cos(x) - cosine of x

double cosh(x) - hyperbolic cosine of x

double exp(x) - exponential function of x

double fabs(x) - (floating) absolute value of x

double floor(x) - largest integer not greater than x

double fmod(x, y) - (floating) modulo function

double frexp(x, pi) - normalized fraction

double ldexp(x, n) - computes x times 2**n

double log(x) - natural log of x

double logl0(x) - base-10 log of x

double modf(x, pd) - compute integer and fractional part of x

double pow(x, y) - raise x to the power y

double sin(x) - sine of x

double sinh(x) - hyperbolic tangent of x

double sqrt(x) - square root of x

double tan(x) - tangent of x

double tanh(x) - hyperbolic tangent of x

In this tabulation, these are the types of the parameters:

double x, y;

double *pd;

int n;

int *pi;

These are the math functions that are declared in <ctype.h> in recent C libraries:

int isalnum(c) - is c a letter or a digit?

int isalpha(c) - is c a letter?

int iscntrl(c) - is c a non-printing character (other than space)?

int isdigit(c) - is c a digit?

int isgraph(c) - is c a printing character (other than space)?

int islower(c) - is c a lower-case letter?

int isprint(c) - is c a printing character (including space)?

int ispunct(c) - is c a punctuation character?

int isspace(c) - is c a whitespace character?

int isupper(c) - is c an upper-case letter?

int isxdigit(c) - is c a hexadecimal digit?

int tolower(c) - convert upper-case c to lower case

int toupper(c) - convert lower-case c to upper case

Just to be sure that you understand the "sandwich rule," jot down the variable name and type for the following declarations.

DECLARATION VARIABLE NAME TYPE ("DECLARED")

short **ap[5]; ap short ** [5]

long (*pf)[2]; pf long (*)[2]

double **pps; pps double **