EG2069 - Course Notes and Guides for Programming in C
Week 2
Iteration Statements - Loops
So far, our programs have consisted of
sequences of statements, one executed after the other with groups of statements
being executed if certain conditions are met (if statements). Now we
look at a group of statements that permit their contents to be repeated while,
or until a condition exists or is met.
The while statement.
The syntax of the while statement is as
follows:
while (expression)
statement; |
and of course, there
is a version involving a block of more than one statement:
while (expression)
{ statement-1; ... statement-n;
} |
What happens is, as follows:
- The expression in the brackets is evaluated and hopefully delivers a 0
(false) or non-zero (true result)
- If true, the statement of contents of the block are executed. We then go
back to step 1.
- If false, then statement or contents of the block are ignored and
execution proceeds beyond the while statement.
Let's consider a
program that makes use of a loop to tabulate a simple function's arguments and
its values. Enter this program into a file called tabulate.c:
#include <stdio.h>
main() { double x, y;
x = -1.0;
while (x <=
1.0) { y =
2.0*x*x - 4*x + 8.0;
printf("%4.1lf
%6.2lf\n",x,y);
x += 0.1;
} } |
Compile and
execute the program as you normally would:
cc tabulate.c -o
tabulate |
What happens? Looking at the parts of
the program in red, the first assignment statement sets x to be the
value -1.0. The condition x <= 1.0 will initially be true so the
contents of the loop will be executed. The last statement in the loop's block
adds 0.1 to the value of x. The next time around the loop, x
is -0.9 so the condition will still be true (x will still be less than, or equal
to, 1.0). It is only when the value of x accumulates upto 1.1, that the
condition will fail and the body of the loop will be forgotten.
The other new feature contained in this program are the digits between the
% and lf characters in the format string of the
printf statement. In the general case, %m.nlf
will cause a number to be printed (of the double type) in m character positions
of which n digits will be displayed after the decimal point. Two character
positions will be taken by the decimal point and a negative sign so it is
important to include these when deciding on a value for m. Modify your program
in tabulate.c so that it prints suitable column headings prior to entering the
loop. You may want to increase the separation of the columns by increasing the
number of spaces in the format string (in the printf statement within the loop).
The for statement.
Another way of expressing the loop in the
above program is with a for statement, the basic format of which is as follows:
for (expression-1 ;
expression-2 ; expression-3 ) statement;
for (expression-1 ; expression-2 ; expression-3
) { statements in a
block } |
Before the
loop is executed, expression-1 is evaluated (which is usually an
assignment statement). If expresion-2 delivers a true (non-zero) result,
the body of the loop is executed, expression-3 is then evaluated, and the loop
returns again to checking the value of expression-2.
The above program can be better expressed using a for statement, because of
the conditions (initial, continuation and final) are expressed entirely within
the top part of the for statement, where it is easier to see everything that is
likely to affect the way in which the loop works:
#include <stdio.h>
main() { double x, y;
for (x = -1.0 ; x <= 1.0 ; x+=0.1)
{ y = 2.0*x*x - 4*x +
8.0;
printf("%4.1lf
%6.2lf\n",x,y); }
} |
Note that the while loop
and for loop always check the condition before the contents of the loop
are executed so there is the possibility that the contents of the loop may never
be executed. Contrast this with yet another form of loop in C:
The do-while statement.
do { one or more statements in a block; }
while (expression); |
The statements in
the block are executed at least once, because the condition that determines
whether the loop goes around at least one more time is evaluated and tested
after the first loop execution.
Consider an earlier program you wrote in the file roots.c.
With an if statement, it was possible to prevent a division by zero error, if
the user typed in the value zero for the coefficient a. With a do while
loop, it is possible to make the program accept only a non-zero value and to
continually re-ask the user until the correct range of input is typed. Consider
our initial program fragment to get the value of a.
printf("Enter a value for a: ");
scanf("%lf",&a); |
Let's surround this
with a loop to ensure that the following program code never sees a zero value in
a:
do { printf("Enter
a value for a: "); scanf("%lf",&a);
} while (a == 0.0); |
As long
as the user types in a zero value for a, the program will repeat the request to
enter a value for a from the keyboard. It would be considered friendlier if
further code was introduced to tell the user why the value is being rejected and
why the program is not continuing:
do { printf("Enter
a non-zero value for a: "); scanf("%lf",&a);
if (a == 0.0) printf("Value
must be non-zero! Please try again.\n"); } while
(a == 0.0); |
Copy your
file roots.c into a new file called newroots.c using the following command:
and modify the program in
newroots.c so that the program will not proceed with finding the roots of
the quadratic equation until the user has typed a non-zero value for the
coefficient a. You will have to discard the program code associated
with solving the linear equation when a does equal 0.
Loops Within Loops.
The program in tabulate.c produced a table of x and
f(x) values in two columns. Consider the case of tabulating a function of two
variables: x,y and f(x,y). For this, we shall need two loops, one within the
other. The outer loop will be concerned with iterating through successive values
of y. The inner loop will iterate through the same sequence of values of x for
each of those y values. Copy the program that follows into a file called
table.c:
#include <stdio.h> #include
<math.h>
main() { double x, y, z;
for (y = -1.0 ; y <= 1.0 ; y +=
0.1) { for (x =
-1.0 ; x <= 1.0 ; x += 0.1) {
z = cos(2.0*x) * sin(y);
printf("%4.1lf %4.1lf
%7.3lf\n",x,y,z); }
} } |
Compile the
program, remembering that you are using math functions sin and
cos so a -lm is required with the cc command:
Execute table and note what
happens, particularly how the sequence of x repeats itself for each value of y.
Try to modify the program so that it will print out a square table, with columns
representing particular values of x and rows particular values of y, for
example:
f(-1.0,-1.0) f(-0.9,-1.0) f(-0.8,-1.0)
................ f(1.0,-1.0) f(-1.0,-0.9) f(-0.9,-0.9)
f(-0.8,-0.9) f(-1.0,-0.8) ...
... ... f(-1.0,1.0)
..........................................
f(1.0,1.0) |
The table will, of course, have the
values of the function subsituted above. For this to work, you will need to
split the effect of the printf statement in the middle of the loop so that:
- the value of x is not printed at all.
- the \n which forces the output to begin a new line has to be done once
before a new value of y is chosen.
- each value of y need only be printed once prior to a new sequence of x
being considered - this will form a column of row labels (values of y)
To help you, study the following skeleton program involving the loops
only:
for (y = -1.0 ; y <= 1.0 ; y
+= 0.1) { Use
printf to display the value of y but don't use \n
in the format string to start a new
line;
for (x = -1.0 ; x <= 1.0 ; x += 0.1)
{ z = cos(2.0*x) * sin(y);
Use printf to display the
value of z, again don't use \n; }
printf("\n");
} |
The printf statement in
bold type causes a new line to be taken - the cursor moves to the beginning of
it. The printf statements you use to display values of y and z should have the
appropriate forms of %m.nlf in them to control the
width of the number printed and the number of decimal places, for example:
Note the extra space between
f and ". This will prevent a y value from bumping into
following z values that are displayed, ie. will provide the separation between
columns. You will need a similar trailing space or two in the format
string of the printf statement that outputs values of z.
Arrays.
So far, we have looked at simple variables containing integers
and floating point numbers (both float and double types).
Let's consider a simple example of taking three integer variables a, b and c and
writing a section of program that will sort their values are in increasing order
of a, b and c. Try to understand what the following conditional statements do.
if (a>b) { t =
a; a = b; b = t; } if (b>c)
{ t = b; b = c; c = t; }
if (a>b) { t = a; a = b; b =
t; } |
It should be fairly easy to see
that the sequence: t = a; a = b; b = t; uses t as a temporary
variable while the values of a and b are swapped over. Why wouldn't the
sequence: a = b = a; work? The process represented by the three
if statements is the beginnings of something called a bubble-sort.
Assuming that a contains the highest value out of a, b and c, the first two if
statements will result in the initial value of a being bubbled up until
it is placed in c, the original values of b and c being displaced downwards
towards a. The last if statement bubbles up the new value of a, if it is larger
than the new value of b.
Incorporate the above sequence of if statements, with suitable integer
declarations for the variables and input/output statements (printf and
scanf) into a program sort1.c to accept three numbers
from the the user, perform the sort, and to output them in increasing numerical
order. Please ensure that you do not call this program sort.c (and therefore
sort) because this conflicts with a standard Unix command called sort.
Compile, run and test the program as usual.
The problem with this program is that it can only ever sort three numbers and
not a potentially variable quantity of values. For each additional value, you
would need another variable name and another series of if statements. Using
arrays and looping statements makes things much easier to code and handle:
Array Declarations.
Let's consider a very simple program that will
permit the user to input a number of values into an array and output their
values again.
#include <stdio.h>
main() { int i, v[6];
for (i=0 ; i<6 ; i++) {
printf("Enter value for v[%d]: ",i);
scanf("%d",&v[i]); }
for (i=0 ; i<6 ; i++) {
printf("v[%d] = %d\n",i,v[i]); }
} |
It's a rather pointless program but
it serves to illustrate several points.
- an array is declared in the same way as an ordinary scalar variable except
that a square-bracketted constant is typed after the variable
name. [6] declares 6 slots or array elements within the
variable v.
- The first element is always the zero element, so each element may be
individually referenced as v[0], v[1], v[2],
v[3], v[4], v[5].
- As can be seen in the example, accessing the individual elements may be
via a potentially complex expression within the square brackets, ie. which
particular elements of the array are manipulated are under the program's
control. In this case, the loops sequentially access each i'th
element in turn.
Other examples of array declarations are as follows:
double x[10], y[10]; int q[100];
double z[10][10]; |
x and y are both
declared to be arrays of double floating point number types, first element
numbered 0, last element numbered 9. q is an integer array with 100 elements in
it, last one being element number 99. z is a two-dimensional array
consisting of 10 rows of 10 elements (all numbered 0 to 9).
Going back to the example array program, let's consider what we can do
between the array input loop and the array output loop to process the data that
has been input. We shall aim to sort the array into increasing order so that
v[0] will contain the smallest value of data and v[5] the
largest.
Assuming a suitable scalar declaration of t, our first step will be to
arrange so that v[5] contains the largest value in the array, having
displaced all smaller values downwards towards v[0]. The fragment of
code to do this is as follows:
for (i = 0; i < 5;
i++) if (v[i] > v[i+1]) {
t = v[i]; v[i] = v[i+1]; v[i+1] = t;
} |
Notice that the loop iterates
over values of i from 0 to 4 (the condition i < 5 ensures that
i==5 never happens). This is because we are always comparing our
current value of v[i] with the one further along, so when i==4, our
comparison is between v[4] and v[4+1], ie. v[5].
We shall generalise the program slightly, so that instead of talking about
bubbling up the largest value out of v[0] to v[5] into
v[5], we'll say that we are looking to place the largest value out of
v[0] to v[k] into v[k]. With this in place (replacing
the red 5 in the above loop with k, suitably declared), we than then consider
looking at the problem of finding the largest value in v[0] to v[k-1], placing
it in v[k-1]. The skeleton part of the program looks like this:
for (k=5; k>0; k--) {
bubble largest of v[0] to v[k] into
v[k]; } |
Create a new
program called bubble.c and combine the input and output
sections of the example program at the top of this subsection with the two loops
above (one will nest inside the other) to create a program that will ask the
user for data to fill the array, then sort and output the array in increasing
numerical value of its elements.
Modify the program to input and sort 10 integer values and test it carefully
to make sure that all the data is being sorted correctly.
Please ensure that you have stuck copies of the programs
tabulate.c, newroots.c, table.c,
sort1.c and bubble.c into your log books along
with the previous week's work. You will
gain extra credit if you have attempted to answer in words some of the thought
questions that have been asked during exercises (clue: they end in question
marks).
Back to main page
Author : Keith Halewood / Helge Nareid Current contact: Gorry Fairhurst G.Fairhurst@eng.abdn.ac.uk