While statement considered harmful

Christopher Bazley, July 2012

Lately I have been editing a well-structured and intelligible program written by a C programmer who does not, for example, use 'goto' to subvert the manifest control flow (cf. ' Go To Statement Considered Harmful', E. W. Dijkstra). However, he 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 idiom. However, consider what happens if another programmer (with a nut allergy) adapts it 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 'item'. This error could have been averted if the first programmer had 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;
}

Any loop in the following form

for( a; b; c ) {
  d;
}

can be rewritten as two statements:

a;
while( b ) {
  d;
  c;
}

However, using '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). In a 'while' loop, the next-iteration expression is is too easily omitted or not executed because it is marooned at the end of the loop's body (which may be many lines distant).

Most modern programming languages offer 'continue', 'for' and 'while' statements. In my view the 'while' statement is not only redundant but harmful in all such languages.