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:
  1. The expression in the brackets is evaluated and hopefully delivers a 0 (false) or non-zero (true result)
  2. If true, the statement of contents of the block are executed. We then go back to step 1.
  3. 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);

(Note that the above is a clue for a week 4 exercise)
Copy your file roots.c into a new file called newroots.c using the following command:
 
cp roots.c newroots.c
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:
 
cc table.c -o table -lm
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: 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:
 
printf("%4.1lf ",y);
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. 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