Tuesday, October 27, 2009

Objective-C Tuesdays: the while loop

After the standard C for loop and the Objective-C 2.0 for...in loop, the while loop is the most common. Unlike the full-featured for loop and the specialized for...in loop, the while is simpler and more free-form.

The while loop starts with the keyword while followed by parentheses containing the condition. After the parentheses, a single statement or block makes up the body of the loop.
/* while loop with a single statement */
while (condition) statement;

/* while loop with a block */
while (condition)
{
  block;
}
Watch out for a hard to spot bug when the while loop is followed by an empty statement created by an extra semicolon:
while (condition); /* <-- warning! empty statement! */
{
  /* this block is not part of the loop */
  /* it's always executed exactly once  */
}
The condition in a while loop is evaluated before the body of the loop is executed, so you can create loops that execute zero or more times. If you want the body of the loop to execute one or more times, you can use a do...while loop instead.

The initialization of the loop counter or other loop variables must be done before the while loop statement; often this means declaring and initializing a local variable:
int i = 0;
while (i < 10)
When using a loop counter, it is important to remember to advance the counter to the next value in the body of the loop. For instance, to log the numbers 0 through 9:
int i = 0;
while (i < 10) {
  NSLog(@"%d", i);
  i++; /* advance the loop counter */
}
If you forget to advance the loop counter, you will create an infinite loop.
int i = 0;
/* warning, infinite loop, i is always 0 */
while (i < 10) {
  NSLog(@"%d", i);
  /* oops, forgot to increment i */
}
The while loop is frequently used when iterating over a stream or sequence of unknown size where a loop counter is unnecessary. For instance, to iterate over a null-terminated string by incrementing the string pointer:
char const *s = "foobar";
while (*s) {        /* while current character isn't zero */
  NSLog(@"%c", *s); /* log the current character */
  s++;              /* increment address in s to next character */
}
Similarly, the idiomatic way to read characters from a standard C FILE is:
FILE *file = /* open file for reading ... */;
int ch;
while ( (ch = fgetc(file)) != EOF) {
  NSLog(@"%c", ch);
}
The loop condition ( (ch = fgetc(file)) != EOF) in this idiom does three things:
  1. fgetc(file) gets the current character and advances to the next
  2. (ch = fgetc(file)) assigns the current character to ch
  3. (ch = ...) != EOF checks that the value of ch is not EOF
Next time, we will look at the do...while loop.

No comments: