USDA ARS

Control structures

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:

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;
        }


IPW documentation / Last revised 20 May 2009 / IPW web site