The most fundamental C control structure is the block. A nested block (i.e., other than the outermost block of a function) should be neither too long to fit on a single printed page, nor so deeply nested most of its statements are multi-line.
Excessive nesting can often be controlled by use of C's "disguised GOTOs"
-- continue
, break
, and return
.
The clarity these provide by minimizing
nesting should always be weighed against their disruption of a simple
top-to-bottom flow of control. goto
itself should hardly ever
be used,
and then only to branch forwards, and never into a block at an equivalent
or deeper nesting level.
Sequences of blocks at the same level (e.g., if-else
) should be
arranged so the
shortest blocks appear first, if their ordering is otherwise unimportant.
The switch
is a special case of if-else
,
and should only be used if:
"case"
s are mutually exclusive
"case"
s are considered is unimportant
if-else
.
The "case"
s in a switch
should each be on a
separate line. The default
case
should always be last, and should always be present; if there is no sensible
default, then the default
case should contain an appropriate
call to the error function "bug"
. The code for each
case
(including default
) should end with either
break
, continue
, return
,
the comment "/* FALL THROUGH */"
, or a call to a
function that never returns (e.g., exit
, error
, etc).
Loops should be designed so that there is minimal interaction between the control statements and the loop body. Control variables should be modified in either a control statement or the loop body, but NOT in both. Control variables should not be assumed to contain any particular value after the loop terminates, unless computing this value is the sole purpose of the loop.
Counters used to control loops should be able to handle the "off-by-one" value that they will usually contain when the loop terminates.
do-while
loops should usually be rewritten as while
or for
loops, unless:
Often there is a temptation to push all the work being done by a loop into
its control statements, especially in a for
loop. This should
be avoided
-- the loop control statements should be limited to controlling whether the
loop is executed:
Example: sum of all elements of array
/* BAD */ for (i = 0, sum = 0; i < n; sum += array[i], ++i) { continue; } /* GOOD */ sum = 0; for (i = 0; i < n; ++i) { sum += array[i]; }
If the desired outcome of a loop is still entirely a consequence of the execution of its control statements, then the loop body should contain a comment indicating that the loop body is empty:
Example: largest power of 2 less than n
for (i = 1; i < n; i *= 2) { /* empty loop body */ ; }
The way in which a loop terminates should be obvious from its syntax. In almost
all cases, a straightforward while
, for
, or
do-while
will suffice. Occasionally
loop termination is controlled by a condition tested within the loop body;
this unusual construct should be clearly commented.
Example (based on [Dromey 1982]):
/* * accumulate x**n (x^n) in product, for integer n > 0 */ product = 1.0; for (; ; ) { /* * extra multiplication if n is odd, * before we divide away the low-order bit */ if (n & 01) { product *= x; } /* * reduce number of multiplications * by exploiting (x**2)**(n/2) == x**n * * terminate before unnecessary x = x**2, * to avoid possible overflow */ if ((n /= 2) == 0) { break; } x *= x; }