Skip to content
Physics and Astronomy
Home Our Teaching Resources C programming Loops
Back to top
On this page
Contents

Loops: while(), for() and do .. while()

Hello John!

In the previous lecture we learnt about logical statements that determine whether or not code gets run. Here we learn about loops which allow sections of code to run zero or more times, with a controlling logical expression.

The while() loop

The while() loop has the form:

  while ( expression ) {
    /* Do something... */
  }

The contents of the loop, which as always may be a single statement or a { ... } block, are executed for as long as the controlling expression is true.

The while() loop repeats as long as the condition is true (non-zero).

If the condition is false the body of the loop never executes at all.

This may sound slightly unintuitive (if there's nothing to do, why have a loop?) but when writing a program we often don't know if there will always be something to loop over and it would be really inconvenient to have to wrap every loop inside an if() statement.

A trivial example

  while (x > 1) 
    x = x / 2.0;

In this rather contrived example, x is halved every time the loop is executed until at last the test condition is false and the loop quits. If x had started with a value of less than or equal to one the loop would not have executed even once.

If the condition is false the body of the loop never executes at all.

  1. Our first loop
  2. To practice the while() loop
  3. Create a new on-line program in a new window, with a suitable title and opening comment.
  4. Declare an integer variable.
  5. Print a suitable message and read in the value of your variable using scanf() and %d. As always don't forget the & before the variable name.
  6. Immediately after reading in the variable name print out its value (as a check) . Build & run and check it's reading in the value OK.
  7. Now create a while() loop whose test is the variable is less than zero and whose body increases the value of the variable by one.
  8. After the loop print out the value of the variable again.
    • If you were to enter a negative value such as -6 what would you expect the final value of the variable to be? How many times would you expect the loop to run?
    • If you were to enter a positive value such as 8 what would you expect the final value of the variable to be? How many times would you expect the loop to run?
  9. Build & run and see if you are correct about the final value of your variable. If not work out why.
  10. In the next exercise we will extend this loop and see how many times the loop runs.

The increment

In one sense while() loops are a bit more complicated and subtle than they first appear. Whilst our initial pseudo-example looks simple, it's clear that the controlling expression can't go on being true forever or else the loop would never stop. Thus the loop must do something to change the value of the controlling expression. We shall refer to this as the increment.

In the above example the increment is the sole statement inside the loop x = x / 2.0; which clearly affects the controlling expression.

Another example of an increment might be a function call (covered in the next lecture) that returns a value to say if the loop should continue, or a call to scanf() (for example a loop that reads in an integer and loops until it's within a certain allowed range of values).

Although it's very unusual, it's possible for the controlling expression to contain its own increment although whether this is wise is another matter altogether.

  1. Multi-statement loop
  2. Building larger loops and seeing some common errors
  3. The loop in the previous exercise did not actually do anything. In this mini-exercise we will be extending the previous program.
  4. Start by putting braces {} around the content of your loop (see the very first example in this lesson).
  5. Now put a printf() statement inside your loop so that it prints out the value of your variable as the loop progresses. (Since there are now three printf() statements in your program, all printing out the value of your loop variable, it would be wise to have some text also inside the message so we can tell their output apart from each other.)
    • How would you expect the output to differ if we put the printf() statement before the line that increments the variable than if we put it after that line? Try it and see.

Understanding the increment is critical to understanding the loop.

Example: a power function

We can use the while loop to write a "poor man's power function" which calculates base to the power of exponent where exponent is a positive integer. We show the core loop of program first, with the complete program following below:

  double result, base = 2.01;
  int exponent = 2;
  result = 1.0;

  while ( exponent > 0 ) {
    result = result * base;
    exponent = exponent - 1;
  }
Step through this code


Here the increment is the line exponent = exponent - 1;
  .

Multiple tests

Although most control expressions are fairly simple we are, of course, allowed to use all the logical operators we can use with the if() expression:

while (test1 && test2) {
 // ...
}

while (test3 || test4) {
 // ...
}

The word bug means an error in the program and debugging refers to fixing problems with the code.

Debugging loops

Loops can easily go wrong with the most common problems being:

  • Loops that never start.
  • Loops that never finish.
  • Loops that repeat the wrong number of times.

Check how a loop starts

When examining any loop it's good to ask "what would happen if the condition were false the very first time it was evaluated?". In this case we are talking about exponent == 0 in which case the loop would never execute. Thus result would be 1.0, the correct answer. If exponent == 1 the loop will execute once, making result equal to base, again the correct answer.

Mentally check how a loop starts

Check how a loop increments and finishes.

As we mentioned above, if exponent == 1 the loop will execute once, making result equal to base, again the correct answer. If exponent == 2 the loop will execute twice, making result equal to base squared, etc.

Mentally check how a loop increments and finishes.

As we discussed in the previous section, the loop above contains an "increment", i.e. a statement that does something that will affect the test the next time round (in this case it's actually a decrement, "increment" is just a generic term):

    exponent = exponent - 1;
  1. Deliberate loop mistakes: the increment
  2. To help us when we make this mistakes when programming
  3. As always, making these mistakes deliberately and seeing what happens will help us to deal with them when we make those mistakes accidentally (and we will1(
  4. "Comment out" the line inside your loop that increments your loop variable. (That is to say, put // at the start of the line so that the line becomes a comment and so is ignored.) You should see the line change colour.
    •  What would you expect to happen to the values of your variable as the loop progresses?
    • If your variable is initially negative, how many times would you expect your loop to run?
  5. Build & run and see if you are correct.
  6. Now uncomment your increment statement but make it subtract 1 from your variable rather than add one to it.
    •  What would you expect to happen to the values of your variable as the loop progresses?
    • If your variable is initially negative, how many times would you expect your loop to run?
  7.  Build & run and see if you are correct.

If in doubt, print it out

If we get the increment wrong the loop may never end, we will have entered an "infinite loop". The program will start and just never end or get on to the next statement. For example we have have mistakenly written:

    exponent = exponent + 1; // Wrong!

If this happens, an easy test is to put a printf() statement inside the loop, such as:

    printf("Exponent: %i result so far: %g\n", exponent, result);

 Notice how we have printed out;
  • The value of the controlling variable and the progress of the loop.
  • A description of what it is: simply printing out anonymous numbers is extremely confusing.

If your program starts and seems to do nothing you may have entered an infinite loop. Try putting a printf() statement in the loop and see what happens.

Another extremely useful tactic is to print out the "input" values at the start of the loop and the result at the end, as demonstrated in our mini-exercises.

  1. Deliberate loop mistakes: the test
  2. To demonstrate a more-subtle error, this time in the test
  3. Fix the code in the previous mini-exercise so that the increment once more increases the value of your loop variable by one. Check it works.
  4. Change the test inside the while() statement from varname < 0 to varname != 0 (where varname is the name of your loop variable).
    • Would you expect this to work OK if the initial value value of you loop variable was negative? Try it.
    • Would you expect this to work OK if the initial value value of you loop variable was positive? Try it
  5. Logical errors like this can be quite subtle and it is equally important to check that things do not occur when we don't want them to as it is to check that they do occur when we do want them to.

A complete program

Here is a short complete program incorporating this loop where we have highlighted the test and the increment:

//
// Poor man's pow() calculation with only positive integer exponents
// Calculates base to the power of exponent
//
#include <stdio.h>

int main() {
  double base = 1.414;
  int exponent = 4;
  double result;

  printf("%f to the power %d is ", base, exponent);

  result = 1.0;
  while ( exponent > 0 ) {
    result = result * base;
    exponent = exponent - 1;
  }

  printf("%f\n", result);
  return 0;
}
Step through this code


The result is:

    1.414000 to the power 4 is 3.997584

Preserving the value of variables

The above example has the effect that the value of the original variable "exponent" is lost (and we have somewhat artificially written the code to handle this). Often however, we need to preserve the original value and so we take a temporary copy of it. This gives the following code, where we have highlighted the three lines that control how many times the loop is executed:

//
// Poor man's pow() calculation with only positive integer exponents
// Calculates base to the power of exponent
//
#include <stdio.h>

int main() {
  double base = 1.414;
  int exponent = 4, tmpexp;
  double result;

  result = 1.0;
  tmpexp = exponent;
  while ( tmpexp > 0 ) {
    result = result * base;
    tmpexp = tmpexp - 1;
  }

  printf("%f to the power %d is %f\n", base, exponent, result);
  return 0;
}
Step through this code


A common pattern

If we look at the parts of the above program that control the execution of the loop we see the following:
  1. Initialise: ( tmpexp = exponent; )
  2. Loop:
    1. Test (tmpexp > 0 ) and if false quit the loop.
    2. Execute the body of the calculation.
    3. Increment. ( tmpexp = tmpexp - 1; )

In our case the loop goes:

Initialise .. test-loop-increment .. test-loop-increment .. test-loop-increment .. test-loop-increment .. test & quit

Notice that the initialisation happens only once for the whole loop.

This Initialise, Test, Increment loop is so common that it has a loop especially for it, the for() loop:

The for() loop

The for() loop rolls all these three into one. It has the following format:

  for (Initialise; Test; Increment ) {
    ... /* Loop body */
  }

Note that the three expressions are separated by semicolons, not commas, and that they occur in the same order as they are used.

for (Initialise; Test; Increment ) separated by semicolons, not commas

The steps in the for() loop are done in exactly the same order as in the equivalent while() loop:

for(Initialise; Test; Increment)

  1. Initialise
  2. Loop:
    1. Test and if false quit the loop.
    2. Execute the body.
    3. Increment.

Just as in the while() loop, if the for() loop test is initially false the body never executes.

Using the for() loop the program now looks like this:

/*
 * Poor man's pow() calculation with only positive integer exponents
 * Calculates "base" to the power of "exponent"
 */
#include <stdio.h>

int main() {
  double base = 1.414;
  int exponent = 4, tmpexp;
  double result;

  result = 1.0;
  for ( tmpexp = exponent; tmpexp > 0; tmpexp = tmpexp - 1 ) 
    result = result * base;


  printf("%f to the power %d is %f\n", base, exponent, result);
  return 0;
}

Where we have chosen to removes the braces { } as there is only one statement within the loop.

The for() loop could be written a little more naturally as:

  for (tmpexp=1; tmpexp <= exponent; tmpexp = tmpexp + 1)

In this case they both yield the same result, although in general this is not always the case of course.

  1. Our first for() loop.
  2. To practice the for() loop
  3. Create a new on-line program in a new window, with a suitable title and opening comment.
  4. Declare an integer variable and then have a for() loop that loops it from 1 to 10 inclusive printing out its value each time:
    1. The first part of the for() statement (initialisation) should initialise the variable to 1.
    2. There is then a semi-colon ; followed by:
    3. The second part of the for() statement (the test) should test that your variable is less than or equal to 10. Remember that the syntax is exactly the same as for an if() statement.
    4. There is then another semi-colon followed by the increment which should increase the value of your variable by one.
    5. The body of your loop should just be a printf() statement that prints out the value of the variable
  5. Notice that the body of your loop should not contain a statement that increments your loop variable like your previous one did, that is now being taken care of by the for() statement.
  6. Build & run and check it works.
    1. What do you think will be the value of your loop variable after the for() loop has finished? Put a suitable printf() statement after the loop to find out.

"In place" arithmetic operators

In the loops we have written so far we have often had steps such as:

    j = j -1;
    tmpexp = tmpexp + 1
    factorial = factorial * n;

which have the same variable on both sides of the equals sign. Similarly, in the wages calculation in the preface we had a step:

  • Add that day's pay to the running-total

This sort of phrase comes naturally to us and C provides a way of doing it directly with the += operator:

  running_total += days_pay;

which means, unsurprisingly, "add the value of days_pay to running_total". Notice there is no space between the + and the =.

When we are at the supermarket the till is constantly doing calculations of the type "add the cost of this item to the current value of the bill".

Just about any operator can have "=" after it:

  x += y;        // add y to x 
  x -= y;        // subtract y from x
  x *= y;        // multiply x by y
  x /= y;        // divide x by y

running_total += days_pay; means "add the value of days_pay to running_total"

The code fragments:
 j += 1;
 k -= 1;

can be further abbreviated to:

  ++j;       /*  add 1 to j */
  --k;       /*  subtract 1 from k */

These last two are used quite often (particularly ++j) so try to use them whenever possible.

++j; means "add 1 to j"

Warning: j++ vs. ++j

There are two slightly different forms of the "++" operator. Both have the same effect when in a statement on their own but are different when used in an expression, such as an assignment.

The statement

  k = ++j;

means "add one to j and then set set k to the (new) value of j".

  k = j++;

means "set k to the (old) value of j and then add one to j".

Exactly the same principle applies to the "--" operator.

Remember the mega-principle and avoid chances to go wrong!

I suggest you don't use "++" inside expressions: it just gives us the chance to go wrong.

  1. Using ++ and experiments
  2. To practice using ++
  3. Replace the increment in your previous loop with an increment of the form ++variablename
  4. Build & run and check it now still works.
    1. We will now practice making the maximum value a variable, rather than a constant:
  5. At the start of main(), before your loop, declare an int variable to contain the maximum value of your loop.
  6. Again before the loop, read in the value of this variable with scanf() (as always print a suitable message to the screen) and change the test of your for() loop so that it now checks your loop variable is less than this maximum value rather than 10.
    1. What would happen if we accidentally read in the value of our maximum after the for() loop?
  7.  Build & run, entering a positive value of the maximum.
  8. Think what would happen if we entered a maximum value that was:
    1. Negative.
    2. Equal to one.
  9. Try them and see if you were right. If not, try to work out why.

Notice how we have taken the opportunity to use a descriptive variable name for our array index.

Declaring loop variables inside a for() statement

The for() (and only the for()) has a further ability: the initialiser can be replaced by a variable declaration which has effect only in that loop, including the loop body. We can therefore simplify the above example slightly:

//
// Poor man's pow() calculation with only positive integer exponents
// Calculates base to the power of exponent
//
#include <stdio.h>

int main() {
  double base = 1.414;
  int exponent = 4;
  double result;

  result = 1.0;
  for ( int tmpexp = exponent; tmpexp > 0; --tmpexp ) 
    result *= base;


  printf("%f to the power %d is %f\n", base, exponent, result);
  return 0;
}
Step through this code


Here we had no need to use tmpexp inside the body of the for() loop, but we could have if we had wanted to. Note that there is now no variable named tmpexp in the body of the main() function, it only exists inside the for() loop.

  1. Internal for() loop variable declaration
  2. To try it out and see a couple of common mistakes
  3. Comment out the declaration of your loop variable and the printf() statement after the end of the loop (we will reuse them later) and instead change the first part of your for() statement so that it declares your variable as in the above example.
  4. Build & run and check it works.
    1. What do you think will happen if we were to uncomment the printf() statement after the loop, without uncommenting the initial declaration of the loop variable? (By "uncomment" we remove the // that we put in to comment-out the statement so that it is now a proper statement again.)
    2. Hint: does your loop variable exist outside of the for() loop?
  5. Try it: uncomment the final printf() statement, Build & run and see what happens.
    1. What would happen if we were now to also uncomment the initial declaration of our loop variable outside of the for() loop?
    2. Would there now be one variable of that name or two?
  6. Try it!
  7. Declaring two variables inside each other like this is extremely confusing. Some compilers will warn about this, or even refuse to compile, depending on the options selected.

See the extended prime number test example.

  1. Specifying both the minimum and maximum values
  2. More for() loop practice
  3. In the previous example we just wanted the loop to repeat a certain number of times. We could have chosen our loop variable to go from 1 to max, from 0 to max - 1,  or even 1001 to 1000 + max(!) In this example we wish to specify both the maximum and minimum value of the loop variable.
  4. Create a new on-line program in a new window, with a suitable title and opening comment.
  5. Much as before, declare a variable for the maximum value of loop variable but this time also declare a variable for the minimum value of the loop variable.
  6.  Read in the values of these two variables from the keyboard and print them to the screen as a test.
    • If you use one scanf() statement to read both variables do not put anything inside the format except for spaces: "%d %d".
  7. Write a for() loop which initialises the value of the loop variable to the desired minimum continues for as long as it is less than the maximum, and printing out the value of the loop variable each time.
  8. Build and run.
    1. What would you expect to happen if you were to enter two numbers where the "minimum" value is greater than the "maximum"?
  9. Try it!

A slight aside: fence post errors

When you first calculated the number of times your loop would execute did you get the answer right or were you off by one? Try the following:

A farmer wishes to make a fence out of fence panels and fence posts in the classic "Post - Panel - Post - Panel - Post "configuration:

|===|===|===|===|===|===|===|

The completed fence must have a a post at each end and the construction is such that the panels are nailed to the posts and butt on to each other so that the posts do not add to the length of the fence.

  1. If the fence is to be 100m long and each panel is 2m long, how many fence panels will the farmer need?
  2. How many fence posts will s/he need?

  1. Answer to the fence-post question
  2. The farmer needs 50 panels and 51 fence-posts. A fence always requires one more post than it does panels.
  3. Consider a fence with just one panel, it will still need two fence posts: |===|

The do { ... } while(); loop

The final, and least used, of C's loops, the do { ... } while();, behaves in exactly the same way as the more widely used while() { ... } loop except that the body of the loop is executed before the logical test, meaning that the loop always executes at least once.

The basic format is:

do  {
 ....
  }  while ( logical-test );

Notice the semi-colon after the while().

Example: successive halving.

The break and continue statements

In the last lecture we met the break statement which breaks out of a switch() statement. The break statement can also break out of a loop (but not an if()).

The break statement breaks out of the innermost switch() statement or loop (but not an if()).

The continue statement

The continue statement goes to the next iteration of the innermost loop (but not an if()).

That is to say, for the while() and do { ... } while(); loops the continue statement goes straight to the next evaluation of the control statement and quits the loop if it evaluates to zero. For a for() it evaluates the increment and then the control statement and quits the loop if that evaluates to zero.

Deliberate infinite loops

The break statement gives us another way to quit a loop and in some circumstances it may be most convenient to deliberately create a loop whose control expression will never be zero and to have a break statement inside it. The following example continues until the user enters a negative number.

#include <stdio.h>
#include <math.h>

int main() {

  while ( 1 ) {
    double x;

    printf("Please enter a positive number, or negative to quit\n");
    scanf("%lg", &x);
    if ( x < 0 )
      break;

    printf("The square root of %g is %g\n", x, sqrt(x));
  }

  return 0;
}
Step through this code


Other popular ways to create an infinite loop include:

  while ( 1 == 1 ) {
    ...
  }

and

  for( ;; ) {
    ...
  }

Note we have still got the two semi-colons inside the for(;;).

This works because in a for() loop if the test is absent it is assumed to be true. The apparent weakness of this construction (it looks strange!) is also its strength: it look so strange it's hard to mistake it for anything else.

  1. The break statement
  2. To practice the break statement.
  3. Modify your previous loop so that the body of the loop also contains an if() statement such that if the count number is greater than a certain limit, say 10, then it calls the break statement as in the previous example.
  4. Build & run, checking it for both possibilities (ie one wherethe number of counts asked for is less than the limit, and one where it is greater than the limit).

A final update to the index-card model

The compiler strips out variable names before running the program

The introduction of loops introduces another consequence of the "human computer with numbered index cards" model.

We previously looked at a simple "materials, labour and total cost" program code which stored the value of materials on card 128:

      double materials, labour, total_cost;

  materials = 9.4;
  labour = 11.3;
  total_cost = materials + labour;

Imagine something similar to this code to be inside a loop running thousands of times. If we are the human computer executing the program we will continually be encountering the variable "materials", looking up what its type is (double) and where it is stored (card 128), and then looking on card 128 for the value. In practice we would soon work out that we could save a lot of time by going through the code and generating a "runnable" version where all the references to "materials" had the type card number and card number following it, thus saving us the trouble of continually looking up where "materials" is stored. For example:

Original Humanly executable
materials => materials_double_at_128
labour => labour_double_at_136
total_cost => total_cost_double_at_144

So the statement:

      materials = 9.4
    

becomes:

      materials_double_at_128 = 9.4
      

Whilst this is a bit unwieldy it will clearly be a lot easier for us to execute many times.

Having the original variable name at the start of the new variable name such as materials_double_at_128 makes it easier to understand what the program is doing, but it's completing unnecessary for actually executing the instructions. Therefore the compiler on our actual computer goes one step further and removes the variable names altogether:

Original Computer executable
materials => double_at_128
labour => double_at_136
total_cost => cost_double_at_144
  1. Before the next lesson spend some time getting used to the idea that "x is just another word for 'the float written on card 20'".
  2. Revisit some of the stepped examples, choose "Advanced options" if necessary and then try the combined and/or Computer views.

Summary

The text of each key point is a link to the place in the web page.

Log in
                                                                                                                                                                                                                                                                       

Validate   Link-check © Copyright & disclaimer Privacy & cookies Share
Back to top