Lately I have been editing an otherwise well-structured and intelligible program written by a C programmer who habitually iterates over linked lists using the following idiom:
int print_shopping_list( listitem_t *list, int max_price ) { int found_count = 0; listitem_t *item = list; while (item != NULL) { if ( item->price <= max_price ) { printf( "Buy %s priced at %d pence", item->name, item->price ); found_count++; } item = item->next; } return found_count; }
Superficially, there is nothing wrong with this. However, consider what happens if another programmer (with a nut allergy) adapts the program to reject peanuts:
int print_shopping_list( listitem_t *list, int max_price ) { int found_count = 0; listitem_t *item = list; while (item != NULL) { if ( item->type == ITEMTYPE_PEANUTS ) { continue; /* No peanuts because of my allergy */ } if ( item->price <= max_price ) { printf( "Buy %s priced at %d pence", item->name, item->price ); found_count++; } item = item->next; } return found_count; }
Oh dear! The modified program will never terminate if it
encounters peanuts in the shopping list, because
continue
jumps to the next loop iteration without
assigning a new value to the current item pointer.
This error could have been averted, had the original
programmer used a for
statement instead of
while
:
int print_shopping_list( listitem_t *list, int max_price ) { int found_count = 0; for ( listitem_t *item = list; item != NULL; item = item->next ) { if ( item->type == ITEMTYPE_PEANUTS ) { continue; /* No peanuts because of my allergy */ } if ( item->price <= max_price ) { printf( "Buy %s priced at %d pence", item->name, item->price ); found_count++; } } return found_count; }
The above code works because the third expression of a
for
statement (to advance to the next iteration) is
executed after a continue
statement.
Any loop in the following form:
for( a; b; c ) { d; }
can be rewritten as multiple statements:
a; while( b ) { d; c; }
Most modern programming languages offer
continue
, for
and while
statements. In my view the while
statement
should never be used for loops that require separate
expressions to test for termination and to advance to the
next iteration.
In a while
loop, the next-iteration expression
is too easily omitted or not executed if it is in a separate
statement marooned at the end of the loop's body (which may
be many lines distant).
for
instead of while
makes
programs more concise, more robust, and easier to read because
all of the expressions relating to a loop are on the same line
(or a few consecutive lines).