Loops: while(), for() and do .. while()
Comments and questions to John Rowe.
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 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.
- Our first loop
- To practice the while() loop
- Create a new on-line program in a new window, with a suitable title and opening comment.
- Declare an integer variable.
- 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.
- 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.
- 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.
- 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?
- Build & run and see if you are correct about the final
value of your variable. If not work out why.
- In the next exercise we will extend this loop
and see how many times the loop runs.
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.
- Multi-statement loop
- Building larger loops and seeing some common errors
- The loop in the previous exercise did not
actually do anything. In this mini-exercise we will be
extending the previous program.
- Start by putting braces {} around the
content of your loop (see the very first example in this
lesson).
- 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;
.
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.
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;
- Deliberate loop mistakes: the increment
- To help us when we make this mistakes when programming
- 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(
- "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?
- Build & run and see if you are correct.
- 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?
- 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.
- Deliberate loop mistakes: the test
- To demonstrate a more-subtle error, this time in the test
- 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.
- 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
- 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:
#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
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:
#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:
- Initialise: ( tmpexp =
exponent; )
- Loop:
- Test (tmpexp > 0
) and if false quit the loop.
- Execute the body of the calculation.
- 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 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)
- Initialise
- Loop:
- Test and if false quit the
loop.
- Execute the body.
- 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:
#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.
- Our first for() loop.
- To practice the for() loop
- Create a new on-line program in a new window, with a suitable title and opening comment.
- Declare an integer variable and then have a for() loop that
loops it from 1 to 10 inclusive printing out its value each
time:
- The first part of the for() statement
(initialisation) should initialise the variable to 1.
- There is then a semi-colon ; followed by:
- 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.
- There is then another semi-colon followed by the increment
which should increase the value of your variable by one.
- The body of your loop should just be a printf()
statement that prints out the value of the variable
- 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.
- Build & run and check it works.
- 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 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.
- Using ++ and experiments
- To practice using ++
- Replace the increment in your previous loop with
an increment of the form ++variablename
- Build & run and check it now still works.
- We will now practice making the maximum value a variable,
rather than a constant:
- At the start of main(), before your loop, declare an int
variable to contain the maximum value of your loop.
- 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.
- What would happen if we accidentally read in the value of
our maximum after the for() loop?
- Build & run, entering a positive value of the
maximum.
- Think what would happen if we entered a maximum value that
was:
- Negative.
- Equal to one.
- 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.
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:
#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.
- Internal for() loop variable declaration
- To try it out and see a couple of common mistakes
- 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.
- Build & run and check it works.
- 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.)
- Hint: does your loop variable exist outside of the for()
loop?
- Try it: uncomment the final printf() statement, Build &
run and see what happens.
- What would happen if we were now to also uncomment the
initial declaration of our loop variable outside of the for()
loop?
- Would there now be one variable of that name or two?
- Try it!
- 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.
- Specifying both the minimum and maximum values
- More for() loop practice
- 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.
- Create a new on-line program in a new window, with a suitable title and opening comment.
- 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.
- 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".
- 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.
- Build and run.
- What would you expect to happen if you
were to enter two numbers where the "minimum" value is
greater than the "maximum"?
- 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.
- If the fence is to be 100m long and each panel is 2m long,
how many fence panels will the farmer need?
- How many fence posts will s/he need?
- Answer to the fence-post question
- The farmer needs 50 panels and 51 fence-posts. A fence
always requires one more post than it does panels.
- Consider a fence with just one panel, it will still need two
fence posts: |===|
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.
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.
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.
- The break statement
- To practice the break statement.
- 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.
- 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).
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
|
- 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'".
- Revisit some of the stepped examples, choose "Advanced options"
if necessary and then try the combined and/or Computer views.
The text of each key point is a link to the place in the web page.