• No results found

4.7 Statements and control structures

4.7.2 Compound statements

4.7.2.2 Loops

4.7.2.2.1 The while loop Awhileloop tests if a condition is true, and if so, executes its body. It then tests the condition is true again, and keeps executing the body as long as it is. Here’s a program that deletes every occurrence of the letterefrom its input.

#include <stdio.h>

int

main(int argc, char **argv) {

int c;

while((c = getchar()) != EOF) {

switch(c) { case 'e': case 'E': break; default: putchar(c); break; } } return 0; }

Note that the expression inside the while argument both assigns the return value of getchartocand tests to see if it is equal toEOF(which is returned when no more input characters are available). This is a very common idiom in C programs. Note also that even thoughcholds a single character, it is declared

as anint. The reason is thatEOF(a constant defined instdio.h) is outside the normal character range, and if you assign it to a variable of typechar it will be quietly truncated into something else. Because C doesn’t provide any sort of exception mechanism for signalling unusual outcomes of function calls, designers of library functions often have to resort to extending the output of a function to include an extra value or two to signal failure; we’ll see this a lot when the null pointer shows up in the chapter onpointers.

4.7.2.2.2 The do..while loop The do..while statement is like thewhile statement except the test is done at the end of the loop instead of the beginning. This means that the body of the loop is always executed at least once.

Here’s a loop that does a random walk until it gets back to 0 (if ever). If we changed thedo..whileloop to awhileloop, it would never take the first step, becauseposstarts at 0.

#include <stdio.h> #include <stdlib.h> #include <time.h>

int

main(int argc, char **argv) {

int pos = 0; /* position of random walk */

srandom(time(0)); /* initialize random number generator */ do {

pos += random() & 0x1 ? +1 : -1; printf("%d\n", pos);

} while(pos != 0);

return 0; }

examples/statements/randomWalk.c

Thedo..whileloop is used much less often in practice than thewhileloop. It is theoretically possible to convert ado..whileloop to awhileloop by making an extra copy of the body in front of the loop, but this is not recommended since it’s almost always a bad idea to duplicate code.

4.7.2.2.3 The for loop Theforloop is a form of syntactic sugar that is used when a loop iterates over a sequence of values stored in some variable (or variables). Its argument consists of three expressions: the first initializes the variable and is called once when the statement is first reached. The second is the

test to see if the body of the loop should be executed; it has the same function as the test in awhileloop. The third sets the variable to its next value. Some examples:

/* count from 0 to 9 */ for(i = 0; i < 10; i++) {

printf("%d\n", i); }

/* and back from 10 to 0 */ for(i = 10; i >= 0; i--) {

printf("%d\n", i); }

/* this loop uses some functions to move around */

for(c = firstCustomer(); c != END_OF_CUSTOMERS; c = customerAfter(c)) { helpCustomer(c);

}

/* this loop prints powers of 2 that are less than n*/ for(i = 1; i < n; i *= 2) {

printf("%d\n", i); }

/* this loop does the same thing with two variables by using the comma operator */ for(i = 0, power = 1; power < n; i++, power *= 2) {

printf("2^%d = %d\n", i, power); }

/* Here are some nested loops that print a times table */ for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { printf("%d*%d=%d ", i, j, i*j); } putchar('\n'); }

Aforloop can always be rewritten as awhileloop.

for(i = 0; i < 10; i++) { printf("%d\n", i); }

/* is exactly the same as */

i = 0;

printf("%d\n", i); i++;

}

4.7.2.2.4 Loops with break, continue, and goto Thebreakstatement immediately exits the innermmost enclosing loop orswitchstatement.

for(i = 0; i < n; i++) { openDoorNumber(i); if(boobyTrapped()) { break; } }

Thecontinuestatement skips to the next iteration. Here is a program with a loop that iterates through all the integers from -10 through 10, skipping 0: #include <stdio.h>

/* print a table of inverses */

#define MAXN (10)

int

main(int argc, char **argv) {

int n;

for(n = -MAXN; n <= MAXN; n++) {

if(n == 0) continue; printf("1.0/%3d = %+f\n", n, 1.0/n); } return 0; } examples/statements/inverses.c

Occasionally, one would like to break out of more than one nested loop. The way to do this is with agoto statement.

for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { doSomethingTimeConsumingWith(i, j); if(checkWatch() == OUT_OF_TIME) { goto giveUp; } } }

giveUp:

puts("done");

The target for thegotois alabel, which is just an identifier followed by a colon and a statement (the empty statement;is ok).

The gotostatement can be used to jump anywhere within the same function body, but breaking out of nested loops is widely considered to be its only genuinely acceptable use in normal code.

4.7.2.3 Choosing where to put a loop exit Choosing where to put a loop exit is usually pretty obvious: you want it after any code that you want to execute at least once, and before any code that you want to execute only if the termination test fails.

If you know in advance what values you are going to be iterating over, you will most likely be using aforloop:

for(i = 0; i < n; i++) { a[i] = 0;

}

Most of the rest of the time, you will want awhileloop:

while(!done()) { doSomething(); }

The do..while loop comes up mostly when you want to try something, then try again if it failed:

do {

result = fetchWebPage(url); } while(result == 0);

Finally, leaving a loop in the middle using break can be handy if you have something extra to do before trying again:

for(;;) { result = fetchWebPage(url); if(result != 0) { break; } /* else */

fprintf(stderr, "fetchWebPage failed with error code %03d\n", result); sleep(retryDelay); /* wait before trying again */

}