Wrox Press C++ Tutorial


Repeating a Block of Statements

The ability to repeat a group of statements is fundamental to most applications. Without this ability, an organization would need to modify the payroll program every time an extra employee was hired, and you would need to reload Tetris every time you wanted to play another game. So let's first understand how a loop works.

What is a Loop?

A loop executes a sequence of statements until a particular condition is true (or false). We can actually write a loop with the C++ statements that we have met so far. We just need an if and the dreaded goto. Look at this example:

// EX2_07.CPP
// Creating a loop with an if and a goto
#include <iostream>
using namespace std;

int main()
{
   int i = 0, sum = 0;
   const int max = 10;
   i = 1;

loop:
   sum += i;             // Add current value of i to sum
   if(++i <= max)
      goto loop;         // Go back to loop until i = 11

   cout << endl
        << "sum = " << sum
        << endl
        << "i = " << i
        << endl;

   return 0;
}

This example accumulates the sum of integers from 1 to 10. The first time through the sequence of statements, i is 1 and is added to sum which starts out as zero. In the if, i is incremented to 2 and, as long as it is less than or equal to max, the unconditional branch to loop occurs and the value of i, now 2, is added to sum. This continues with i being incremented and added to sum each time, until finally, when i is incremented to 11 in the if, the branch back will not be executed. If you run this example, you will get this output:

This shows quite clearly how the loop works. However, it uses a goto and introduces a label into our program, both of which are things we should avoid if possible. We can achieve the same thing, and more, with the for loop. Let's have a go at that now.

Try It Out - Using the for Loop

We can rewrite the last code fragment as a working example using a for loop:

// EX2_08.CPP
// Summing integers with a for loop
#include <iostream>
using namespace std;

int main()
{
   int i = 0, sum = 0;
   const int max = 10;

   for(i = 1; i <= max; i++)   // Loop specification
      sum += i;                // Loop statement

   cout << endl
        << "sum = " << sum
        << endl
        << "i = " << i
        << endl;

   return 0;
}

How It Works

If you compile and run this, you will get exactly the same output as you did in the previous example, but the code is much simpler here. The conditions determining the operation of the loop appear in parentheses after the keyword for. There are three expressions that appear within the parentheses:

Actually, this loop is not exactly the same as the version in Ex2_07.cpp. You can demonstrate this if you set the value of max to 0 in both programs and run them again. Then, you will find that the value of sum is 1 in Ex2_07.cpp and 0 in Ex2_08.cpp, and the value of i differs too. The reason for this is that the if version of the program always executes the loop at least once, since we don't check the condition until the end. The for loop doesn't do this, because the condition is actually checked at the beginning.

The general form of the for loop is:

for (initializing_expression; test_expression; increment_expression)

loop_statement;

Of course, loop_statement can be a block between braces. The sequence of events in executing the for loop is shown here:

As we have said, the loop statement shown in the diagram can also be a block of statements. The expressions controlling the for loop are very flexible. You can even put multiple expressions for each, separated by the comma operator. This gives you a lot of scope in applying the for loop.

Variations on the for Loop

Most of the time, the expressions in a for loop are used in a fairly standard way:

However, you are not obliged to use these expressions in this way and quite a few variations are possible.

The initialization expression in a for loop can also include a declaration for a loop variable. Using our previous example, we could have written the loop to include the declaration for the loop counter i:

for(int i = 1; i <= max; i++)   // Loop specification
   sum += i;                    // Loop statement

Naturally, the original declaration for i would need to be omitted in the program. If you make this change to the last example, you will find that it runs exactly as before, but there is something odd about this. A loop has a scope which extends from the for expression to the end of the body of the loop, which of course can be a block of code between braces, as well as just a single statement. The counter i is now declared within the loop scope, but we are still able to refer to it in the output statement, which is outside the scope of i. This is because a special extension has been allowed for loop counters to extend their scope to the scope enclosing the loop.

In Visual C++ 6.0, a counter which is declared within a for loop expression remains in scope after the loop has finished executing. However, I recommend that you don't write programs that rely on the scope of the counter extending beyond the end of the loop. This is because the final draft of the new ANSI/ISO standard for C++ says that the scope of the loop counter should not extend beyond the end of the loop itself; something which is likely to be implemented by future C++ compilers. If you need to use the value in the counter after the loop has executed, then declare the counter outside the scope of the loop.

You can also omit the initialization expression altogether. If we initialize i appropriately in the declaration, we can write the loop as:

int i = 1;
for( ; i <= max; i++)           // Loop specification
   sum += i;                    // Loop statement

You still need the semicolon that separates the initialization expression from the test condition for the loop. In fact, both semicolons must be in place. If you omit the first semicolon, the compiler will be unable to decide which expression has been omitted.

This flexibility also applies to the contents of the increment expression. For example, we can place the loop statement in the last example inside the increment expression - the loop becomes:

for(i = 1; i <= max; sum += i++);     // The whole loop

We need the semicolon after the closing parentheses, to indicate that the loop statement is now empty. If you omit this, the statement immediately following this line will be interpreted as the loop statement.

Try It Out - Using Multiple Counters

You can use the comma operator to include multiple counters in a for loop. We can show this in operation in the following program:

// EX2_09.CPP
// Using multiple counters to show powers of 2
#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
   long i = 0, power = 0;
   const int max = 10;

   for(i = 0, power = 1; i <= max; i++, power += power)
      // Loop statement
      cout << endl
           << setw(10) << i << setw(10) << power;  

   cout << endl;
   return 0;
}

How It Works

We initialize two variables in the initialization section of the for loop, separated by the comma operator, and increment each of them in the increment section. Clearly, you can put as many expressions as you like in each position.

You can even specify multiple conditions, separated by commas, in the test part of the for loop; but only the right-most condition will affect when the loop ends.

Note that the assignments defining the initial values for i and power are expressions, not statements. A statement always ends with a semicolon.

For each increment of i, the value of the variable power is doubled by adding it to itself. This produces the powers of two that we are looking for, and so the program will produce the following output:

The setw() manipulator that we saw in the previous chapter is used to align the output nicely. We have #included iomanip so that we can use setw().

Try It Out - The Infinite for Loop

If you omit the test condition then the value is assumed to be true, so the loop will continue indefinitely unless you provide some other means of exiting from it. In fact, if you like, you can omit all the expressions in the parentheses after for. This may not seem to be very useful, but in fact, quite the reverse is true. You will often come across situations where you want to execute a loop a number of times, but you do not know in advance how many iterations you will need. Have a look at this:

// EX2_10.CPP
// Using an infinite for loop to compute an average
#include <iostream>
using namespace std;

int main()
{
   double value = 0.0; // Value entered stored here
   double sum = 0.0;   // Total of values accumulated here
   int i = 0;          // Count of number of values
   char indicator = 'n';    // Continue or not?

   for(;;)                  // Infinite loop
   {
      cout << endl
           << "Enter a value: ";
      cin >> value;   // Read a value
      ++i;                  // Increment count
      sum += value;         // Add current input to total

      cout << endl
           << "Do you want to enter another value 
                                     (enter n to end)? ";
      cin >> indicator;     // Read indicator

      if ((indicator == 'n') || (indicator == 'N'))
         break;             // Exit from loop
   }

   cout << endl
        << "The average of the " << i
        << " values you entered is " << sum/i << "."
        << endl;
   return 0;
}

How It Works

This program will compute the average of an arbitrary number of values. After each value is entered you need to indicate whether you want to enter another value, by entering a single character y or n. Typical output from executing this example is:

After declaring and initializing the variables that we're going to use, we start a for loop with no expressions specified, so there is no provision for ending it here. The block immediately following is the subject of the loop which is to be repeated.

The loop block performs two basic actions:

The first action within the block is to prompt you for input and then read a value into the variable value. The value that you enter is added to sum and the count of the number of values, i, is incremented. After accumulating the value in sum, you are prompted to enter 'n' if you have finished. The character that you enter is stored in the variable indicator for testing against 'n' or 'N' in the if statement. If neither is found, the loop continues, otherwise a break is executed. The effect of break in a loop is similar to its effect in the context of the switch statement. In this instance, it exits the loop immediately by transferring to the statement following the closing brace of the loop block.

Finally, we output the count of the number of values entered and their average, calculated by dividing sum by i. Of course, i will be promoted to double before the calculation, as you will remember from the our earlier discussion of casting.

The continue Statement

There is another statement, besides break, that is used to affect the operation of a loop: the continue statement. This is written simply as:

continue;

Executing continue within a loop starts the next loop iteration immediately, skipping over any statements remaining in the current iteration. We can demonstrate how this works with the following code fragment:

#include <iostream>
using namespace std; 

int main()
{
   int i = 0, value = 0, product = 1;
   for(i = 1; i <= 10; i++)
   {
      cin >> value;
      if(value == 0)           // If value is zero
         continue;             // skip to next iteration
      product *= value;
   }

   cout << "Product (ignoring zeros): " << product
        << endl;
   return 0;
}

This loop reads 10 values with the intention of producing the product of the values entered. The if checks each value entered, and if it is zero, then the continue statement skips to the next iteration. This is so that we don't end up with a zero product if one of the values is zero. Obviously, if a zero value occurred on the last iteration, the loop would end. There are clearly other ways of achieving the same result, but continue provides a very useful capability, particularly with complex loops where you may need to skip to the end of the current iteration from various points in the loop.

The effect of the break and continue statements on the logic of a for loop is illustrated here:

Obviously, in a real situation, the break and continue statements are used with some condition-testing logic to determine when the loop should be exited, or when an iteration of the loop should be skipped. The break and continue statements can also be used with the other kinds of loop which we'll discuss later on in this chapter, where they work in exactly the same way.

Try It Out - Using Other Types in Loops

So far, we have only used integers to count loop iterations. You are in no way restricted as to what type of variable you use to count iterations. Look at this example:

// EX2_11.CPP
// Display ASCII codes for alphabetic characters
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{

   for(char capital='A', small='a'; capital<='Z'; capital++, small++)
      cout << endl
           << "\t" << capital                              // Output capital as character
           << hex << setw(10) << static_cast<int>(capital) // Output capital as hex
           << dec << setw(10) << static_cast<int>(capital) // Output capital as decimal
           << "    " << small                              // Output small as character
           << hex << setw(10) << static_cast<int>(small)   // Output small as hex
           << dec << setw(10) << static_cast<int>(small);  // Output small as decimal

   cout << endl;
   return 0;
}

How It Works

The loop in this example is controlled by the char variable capital which we declare along with the variable small in the initializing expression. We also increment both variables in the increment part, so that the value of capital varies from 'A' to 'Z', and the value of small correspondingly varies from 'a' to 'z'.

The loop contains just one output statement spread over seven lines. The first line:

cout << endl

starts a new line on the screen. On each iteration, after outputting a tab character, the value of capital is displayed three times: as a character, as a hexadecimal value and as a decimal value. We insert the manipulator hex which causes succeeding data values to be displayed as hexadecimal values for the second output of capital, and we then insert the manipulator dec to cause succeeding values to be output as decimal once more. We get the char variable capital to output as a numeric value by casting it to int, using the static_cast<>() which we saw in the last chapter. The value of small is output in a similar way. As a result, the program will generate the following output:

    A        41        65    a        61        97
    B        42        66    b        62        98
    C        43        67    c        63        99
    D        44        68    d        64       100
    E        45        69    e        65       101
    F        46        70    f        66       102
    G        47        71    g        67       103
    H        48        72    h        68       104
    I        49        73    i        69       105
    J        4a        74    j        6a       106
    K        4b        75    k        6b       107
    L        4c        76    l        6c       108
    M        4d        77    m        6d       109
    N        4e        78    n        6e       110
    O        4f        79    o        6f       111
    P        50        80    p        70       112
    Q        51        81    q        71       113
    R        52        82    r        72       114
    S        53        83    s        73       115
    T        54        84    t        74       116
    U        55        85    u        75       117
    V        56        86    v        76       118
    W        57        87    w        77       119
    X        58        88    x        78       120
    Y        59        89    y        79       121
    Z        5a        90    z        7a       122

You can also use a floating point value as a loop counter. An example of a for loop with this kind of counter is:

double a = 0.3, b = 2.5;
for(double x = 0.0; x <= 2.0; x += .25)
   cout << "\n\tx = " << x
        << "\ta*x + b = " << a*x + b;

This calculates the value of a*x+b for values of x from 0.0 to 2.0 in steps of 0.25. However, you need to take care when using a floating point counter in a loop. Many decimal values are not represented exactly in binary floating point, so discrepancies can build up with accumulative values.

The while Loop

A second kind of loop in C++ is the while loop. Where the for loop is primarily used to repeat a statement or a block for a prescribed number of iterations, the while loop will continue as long as a specified condition is true. The general form of the while loop is:

while(condition)
   loop_statement;

where loop_statement will be executed repeatedly as long as the condition expression has the value true. Once the condition becomes false, the program continues with the statement following the loop. Of course, a block of statements between braces could replace the single loop_statement. The logic of the while loop can be represented like this:

Try It Out - Using the while Loop

We could rewrite our program to compute averages (Ex2_10.cpp) to use the while loop:

// EX2_12.CPP
// Using a while loop to compute an average
#include <iostream>
using namespace std;

int main()
{
   double value = 0.0; // Value entered stored here
   double sum = 0.0;   // Total of values accumulated here
   int i = 0;          // Count of number of values
   char indicator = 'y';   // Continue or not?

   while(indicator == 'y') // Loop as long as y is entered
   {
      cout << endl
           << "Enter a value: ";
      cin >> value;        // Read a value

      ++i;                 // Increment count
      sum += value;        // Add current input to total

      cout << endl
           << "Do you want to enter another value 
                                      (enter n to end)? ";

      cin >> indicator;             // Read indicator
   }

   cout << endl
        << "The average of the " << i
        << " values you entered is " << sum/i << "."
        << endl;
   return 0;
}

How It Works

For the same input, this version of the program will produce the same output as before. One statement has been updated, and another has been added - they are highlighted above. The for loop statement has been replaced by the while statement and the test for indicator in the if has been deleted, as this function is performed by the while condition. You need to initialize indicator with 'y' in place of the 'n' which appeared previously - otherwise the while loop will terminate immediately. As long as the condition in the while returns true, then the loop continues. You can put any expression resulting in true or false as the loop condition. The example would be a better program if the loop condition were extended to allow 'Y' to be entered to continue the loop as well as 'y'. Modifying the while to the following:

while((indicator=='y') || (indicator=='Y'))

would do the trick.

You can also create an infinite while loop by using a condition that is always true. This can be written as follows:

while(1)
{
...
}

Here, as elsewhere, the integer value 1 is converted to the bool value true. Naturally, the same requirement applies here as in the case of the infinite for loop: namely, that there must be some way of exiting the loop within the loop block.

The do-while Loop

The do-while loop is similar to the while loop in that the loop continues as long as the specified loop condition remains true. The main difference is that the condition is checked at the end of the loop-in the case of the while loop and the for loop, the condition is checked at the beginning of the loop. Thus, the do-while loop statement is always executed at least once. The general form of the do-while loop is:

do
{
   loop_statements;
}while(condition);

The logic of this form of loop is shown here:

We could replace the while loop in the last version of the program to calculate an average with a do-while loop:

do
   {
      cout << endl
           << "Enter a value: ";
      cin >> value;         // Read a value
      ++i;                  // Increment count
      sum += value;         // Add current input to total
      cout << "Do you want to enter another value 
                                     (enter n to end)?";
      cin >> indicator;     // Read indicator
   } while((indicator=='y') || (indicator=='Y'));

There's little to choose between them, except that this version doesn't depend on the initial value set in indicator for correct operation. As long as you want to enter at least one value, which is not unreasonable for the calculation in question, this version of the loop is preferable.

Nested Loops

You can nest one loop inside another. This technique typically applied to repeating actions at different levels of classification. An example might be calculating the total marks for each student in a class, then repeating the process for each class in a school.

Try It Out - Nested Loops

We can illustrate the effects of nesting one loop inside another by calculating a simple formula. A factorial of an integer is the product of all the integers from 1 to the integer in question; so the factorial of 3, for example, is 1 times 2 times 3, which is 6. The following program will compute the factorial of integers that you enter (until you've had enough):

// EX2_13.CPP
// Demonstrating nested loops to compute factorials
#include <iostream>
using namespace std;

int main()
{
   char indicator = 'n';
   long value = 0,
        factorial = 0;

   do
   {
      cout << endl
           << "Enter an integer value: ";
      cin >> value;
      factorial = 1;

      for(int i = 2; i<=value; i++)
         factorial *= i;

      cout << "Factorial " << value << " is " << factorial;

      cout << endl
           << "Do you want to enter another value 
                                        (y or n)? ";
      cin >> indicator;
   } while((indicator=='y') || (indicator=='Y'));

   return 0;
}

How It Works

If you compile and execute this example, the typical output produced is:

Factorial values grow very fast. In fact, 12 is the largest input value for which this example produces a correct result. If you run it with larger input values, leading digits will be lost in the result stored in the variable factorial, and you may well get negative values for the factorial.

This situation doesn't cause any error messages, so it is of paramount importance that you are sure that the values you're dealing with in a program can be contained in the permitted range of the type of variable you're using. You also need to consider the effects of incorrect input values. Errors of this kind, which occur silently, can be very hard to find.

The outer of the two nested loops is the do-while loop, which controls when the program ends. As long as you keep entering y or Y at the prompt, the program will continue to calculate factorial values. The factorial for the integer entered is calculated in the inner for loop. This is executed value times to multiply the variable factorial (with an initial value of 1) with successive integers from 2 to value.

Try It Out - Another Nested Loop

If you haven't dealt much with nested loops they can be a little confusing, so let's try another example. This program will generate a multiplication table of a given size:

// EX2_14.CPP
// Using nested loops to generate a multiplcation table
#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
   const int size = 12;          // Size of table
   int i = 0, j = 0;             // Loop counters

   cout << endl                  // Output table title
        << size << " by " << size
        << " Multiplication Table" << endl << endl;
   cout << endl << "    |";

   for(i=1; i<=size; i++)        // Loop to output column headings
      cout << setw(3) << i << "  ";      

   cout << endl;                 // Newline for underlines

   for(i=0; i<=size; i++)
      cout << "_____";           // Underline each heading

   for(i=1; i<=size; i++)        // Outer loop for rows
   {
      cout << endl
           << setw(3) << i << " |";  // Output row label

      // Inner loop to output the rest of the row
      for(j=1; j<=size; j++)
         cout << setw(3) << i*j << "  ";
      // End of inner loop
   }                             // End of outer loop

   cout << endl;
   return 0;
}

How It Works

If you build this example and execute it, you will see the output shown in the figure below. This shows the output window when execution is complete:

The table title is produced by the first output statement in the program. The next output statement, combined with the loop following it, generates the column headings. Each column will be five characters wide, so the heading value is displayed in a field width of three specified by the setw(3) manipulator, followed by two blanks. The output statement preceding the loop outputs four spaces and a vertical bar above the first column that will contain the row headings. A series of underline characters is then displayed beneath the column headings.

The nested loop generates the main table contents. The outer loop repeats once for each row, so i is the row number. The output statement,


      cout << endl
           << setw(3) << i << " |";  // Output row label

goes to a new line for the start of a row and then outputs the row heading given by the value of i in a field width of three, followed by a space and a vertical bar.

A row of values is generated by the inner loop:


      // Inner loop to output the rest of the row
      for(j=1; j<=size; j++)
         cout << setw(3) << i*j << "  ";
      // End of inner loop

This loop outputs values i*j corresponding to the product of the current row value i, and each of the column values in turn by varying j from 1 to size. So, for each iteration of the outer loop, the inner loop executes size iterations. The values are positioned in the same way as the column headings.

When the outer loop is completed, the return is executed to end the program.


© 1998 Wrox Press