8D. Things to Watch Out For

Watch out: infinite loops

What if the update of i is accidentally omitted from the definition of sum? That is, we write
  //sum(n) returns 1 + 2 + ... + n

  int sum(const int n)
  {
    int s = 0;
    int i = 0;
    while(i < n)
    {
      s = s + i;
    }
    return s;
  }
Doing a hand simulation shows the problem.
     i    s    n
     0    0    3
          0
          0
          0
Since i never changes, the loop does not ever stop. If you suspect that your program is looping forever in Linux, type control-C to stop it.

A debugger, such as gdb, is a good way to find the source of an infinite loop. Start gdb, run your program, stop it with control-C, then to a bt command to see the current function frames, most recently entered frame first. Here is an abbreviated sample session with gdb, where myprogram.cpp contains the bad loop (with n = 3) and ./myprogram is the executable program produced by g++. Symbol ^C means that control-C was pressed.

$ gdb ./myprogram
...
(gdb) run
... ^C
Program received signal SIGINT, Interrupt.
0x0000000000400637 in sum (n=3) at myprogram.cpp:9
9	      s = s + i;
(gdb) bt
#0  0x0000000000400637 in sum (n=3) at myprogram.cpp:9
#1  0x0000000000400655 in main () at myprogram.cpp:16
(gdb) print n
$1 = 3
(gdb) print i
$1 = 0
(gdb) quit
That shows that main called sum with n = 3, and that the program is currently running sum at line 9, which is the line
  s = s + i;
That knowledge should be enough for you to fix the infinite loop.

Watch out: initializing and updating control variables

Variables that are changed by a loop's body are called the loop's control variables.

It is a good idea to identify which variables you want to be control variables before you write a loop. Then, after writing it, check the following.

  1. Did you initialize each of the control variables? They should be initialized immediately before the loop. Do not put anything in between the initialization and the loop. Initializing control variables in the wrong place is a common source of mistakes.

    What if you omit the initialization? Here is the sum function with initialization of i done but no initialization of s.

      //sum(n) returns 1 + 2 + ... + n
    
      int sum(const int n)
      {
        int s;
        int i = 0;
        while(i < n)
        {
          i = i + 1;
          s = s + i;
        }
        return s;
      }
    
    A hand simulation quickly shows the problem.
         i    s    n
         0    ?    3
              ?
    
    Adding a number to a junk value gives a junk value.

  2. Did you change each control variable in the loop body in an appropriate way? If one of the control variables is not changed by the loop body, something is fishy.

  3. Are you changing or using variables other than the control variables in the loop body? If so, make sure that is what you intend to do. Using the wrong variables is another common source of mistakes.

    Here is an example, a modified version of the sum function.

      //sum(n) returns 1 + 2 + ... + n
    
      int sum(const int n)
      {
        int s = 0;
        int i = 0;
        while(i < n)
        {
          i = i + 1;
          s = s + n;
        }
        return s;
      }
    
    Notice that the loop body uses n. It has no business doing that. A careful hand simulation shows what goes wrong.
         i    s    n
         0    0    3
         1    3
         2    6
         3    9
    
    The result, 9, is not correct.

  4. The condition in the while-loop heading typically asks about at least one of the control variables. If it doesn't, ask yourself whether that is sensible for this loop.


Watch out: semicolons

Do not write a semicolon after the while-loop heading. For example,
  int n = 0;
  while(n < 4);
  {
    printf("%i\n", n);
    n++;
  }
keeps going forever. The problem is that the loop body is a semicolon, which is a do nothing statement.

Watch out: else after while

A while-loop does not have an else part. When it ends, it just ends. So do not write
  while(test)
  {
    loop body
  }
  else 
  {
    do something
  }

Watch out: uninitialized variables

A while-loop can perform its body no times. Look at the following function definition.
  int demo(const int n)
  {
    int result;
    int t   = n;
    int sum = 0;
    while(t > 0)
    {
      sum    = sum + t;
      result = sum;
      t--;
    }
    return result;
  }
Notice that, if n ≤ 0, then the loop body is not performed at all, so no value is ever stored into variable 'result'. The function returns a junk value.

That is easy to fix. A little thought shows that variable 'result' is unnecessary. It is just the same as sum. So the following does the job.

  int demo(const int n)
  {
    int t   = n;
    int sum = 0;
    while(t > 0)
    {
      sum = sum + t;
      t--;
    }
    return sum;
  }



Summary

Loops provide many opportunities for making mistakes, so you need to approach them in a disciplined way. Hand simulation is an important tool for ensuring that a loop is correct.