next up previous
Next: Functions Up: Scientific programming in C Previous: Structure of a C

Control statements

The C language includes a wide variety of powerful and flexible control statements. The most useful of these are described in the following.

The if-else statement is used to carry out a logical test and then take one of two possible actions, depending on whether the outcome of the test is true or false. The else portion of the statement is optional. Thus, the simplest possible if-else statement takes the form:

if (expression) statement
The expression must be placed in parenthesis, as shown. In this form, the statement will only be executed if the expression has a nonzero value (i.e., if expression if true). If the expression has a value of zero (i.e., if expression is false) then the statement will be ignored. The statement can be either simple or compound.

The program quadratic.c, listed previously, is incapable of dealing correctly with cases where the roots are complex (i.e., $b^2<4\,a\,c$), or cases where $a=0$. It is good programming practice to test for situations which fall outside the domain of validity of a program, and produce some sort of error message when these occur. An amended version of quadratic.c which uses if-else statements to reject invalid input data is listed below.

/* quadratic1.c */
/*
  Program to evaluate real roots of quadratic equation

     2
  a x  + b x + c = 0

  using quadratic formula

                     2  
  x = ( -b +/- sqrt(b - 4 a c) ) / (2 a)

  Program rejects cases where roots are complex
  or where a = 0.
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int main() 
{
  double a, b, c, d, e, x1, x2;

  /* Read input data */
  printf("\na = ");
  scanf("%lf", &a);
  printf("b = ");
  scanf("%lf", &b);
  printf("c = ");
  scanf("%lf", &c);
 
  /* Test for complex roots */
  e = b * b - 4. * a * c;

  if (e < 0.) 
   {
    printf("\nError: roots are complex\n");
    exit(1);
   }

  /* Test for a = 0. */
  if (a == 0.) 
   {
    printf("\nError: a = 0.\n");
    exit(1);
   }

  /* Perform calculation */
  d = sqrt(e);
  x1 = (-b + d) / (2. * a);
  x2 = (-b - d) / (2. * a);

  /* Display output */
  printf("\nx1 = %12.3e   x2 = %12.3e\n", x1, x2);

  return 0;
}
Note the use of indentation to highlight statements which are conditionally executed (i.e., statements within an if-else statement). The standard library function call exit(1) (header file: stdlib.h) causes the program to abort with an error status. Execution of the above program for the case of complex roots yields the following output:
a = 4
b = 2
c = 6

Error: roots are complex
%

The general form of an if-else statement, which includes the else clause, is

if (expression)  statement 1  else  statement 2
If the expression has a non-zero value (i.e., if expression is true) then statement1 is executed. Otherwise, statement2 is executed. The program listed below is an extended version of the previous program quadratic.c which is capable of dealing with complex roots.
/* quadratic2.c */
/*
  Program to evaluate all roots of quadratic equation

     2
  a x  + b x + c = 0

  using quadratic formula

                     2  
  x = ( -b +/- sqrt(b - 4 a c) ) / (2 a)

  Program rejects cases where a = 0.
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

int main() 
{
  double a, b, c, d, e, x1, x2;

  /* Read input data */
  printf("\na = ");
  scanf("%lf", &a);
  printf("b = ");
  scanf("%lf", &b);
  printf("c = ");
  scanf("%lf", &c);
 
  /* Test for a = 0. */
  if (a == 0.) 
   {
    printf("\nError: a = 0.\n");
    exit(1);
   }

  /* Perform calculation */
  e = b * b - 4. * a * c;

  if (e > 0.) // Test for real roots
   {  
    /* Case of real roots */
    d = sqrt(e);
    x1 = (-b + d) / (2. * a);
    x2 = (-b - d) / (2. * a);
    printf("\nx1 = %12.3e   x2 = %12.3e\n", x1, x2);
   } 
  else 
   {
    /* Case of complex roots */
    d = sqrt(-e);
    x1 = -b / (2. * a);
    x2 = d / (2. * a);
    printf("\nx1 = (%12.3e, %12.3e)   x2 = (%12.3e, %12.3e)\n", 
           x1, x2, x1, -x2);
   }
  return 0;
}
Note the use of an if-else statement to deal with the two alternative cases of real and complex roots. Note also that the C compiler ignores all characters on a line which occur after the // construct.7 Hence, this construct can be used to comment individual lines in a program. The output from the above program for the case of complex roots looks like:
a = 9
b = 2
c = 2

x1 = (  -1.111e-01,    4.581e-01)   x2 = (  -1.111e-01,   -4.581e-01)
%

The while statement is used to carry out looping operations, in which a group of statements is executed repeatedly until some condition is satisfied. The general form of a while statement is

while  (expression)  statement
The statement is executed repeatedly, as long as the expression is nonzero (i.e., as long as expression is true). The statement can be either simple or compound. Of course, the statement must include some feature that eventually alters the value of the expression, thus providing an escape mechanism from the loop.

The program listed below (iteration.c) uses a while statement to solve an algebraic equation via iteration, as explained in the initial comments.

/* iteration.c */
/* 
   Program to solve algebraic equation

    5      2 
   x  + a x  - b = 0

   by iteration. Easily shown that equation must have at least
   one real root. Coefficients a and b are supplied by user.

   Iteration scheme is as follows:

                    2  0.2
   x    =  ( b - a x  )
    n+1             n

   where x_n is nth iteration. User must supply initial guess for x.
   Iteration continues until relative change in x per iteration is 
   less than eps (user supplied) or until number of iterations exceeds 
   NITER. Program aborts if (b - a x*x) becomes negative. 
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* Set max. allowable no. of iterations */
#define NITER 30   

int main() 
{
  double a, b, eps, x, x0, dx = 1., d;
  int count = 0;

  /* Read input data */
  printf("\na = ");
  scanf("%lf", &a);
  printf("b = ");
  scanf("%lf", &b);
  printf("eps = ");
  scanf("%lf", &eps);

  /* Read initial guess for x */
  printf("\nInitial guess for x = ");  
  scanf("%lf", &x);
  x0 = x;

  while (dx > eps)  // Start iteration loop: test for convergence
   {
    /* Check for too many iterations */
    ++count;
    if (count > NITER) 
     {
      printf("\nError: no convergence\n");
      exit(1);
     }

    /* Reject complex roots */
    d = b - a * x * x;
    if (d < 0.)    
     {
      printf("Error: complex roots - try another initial guess\n");
      exit(1);
     }

    /* Perform iteration */
    x = pow(d, 0.2);
    dx = fabs( (x - x0) / x );
    x0 = x;

    /* Output data on iteration */
    printf("Iter = %3d   x = %8.4f   dx = %12.3e\n", count, x, dx);
   }
  return 0;
}
The typical output from the above program looks like:
a = 3
b = 10
eps = 1.e-6

Initial guess for x = 1
Iter =   1   x =   1.4758   dx =    3.224e-01
Iter =   2   x =   1.2823   dx =    1.509e-01
Iter =   3   x =   1.3834   dx =    7.314e-02
Iter =   4   x =   1.3361   dx =    3.541e-02
Iter =   5   x =   1.3595   dx =    1.720e-02
Iter =   6   x =   1.3483   dx =    8.350e-03
Iter =   7   x =   1.3537   dx =    4.056e-03
Iter =   8   x =   1.3511   dx =    1.969e-03
Iter =   9   x =   1.3524   dx =    9.564e-04
Iter =  10   x =   1.3518   dx =    4.644e-04
Iter =  11   x =   1.3521   dx =    2.255e-04
Iter =  12   x =   1.3519   dx =    1.095e-04
Iter =  13   x =   1.3520   dx =    5.318e-05
Iter =  14   x =   1.3519   dx =    2.583e-05
Iter =  15   x =   1.3520   dx =    1.254e-05
Iter =  16   x =   1.3520   dx =    6.091e-06
Iter =  17   x =   1.3520   dx =    2.958e-06
Iter =  18   x =   1.3520   dx =    1.436e-06
Iter =  19   x =   1.3520   dx =    6.975e-07      
%    

When a loop is constructed using a while statement, the test for the continuation of the loop is carried out at the beginning of each pass. Sometimes, however, it is desirable to have a loop where the test for continuation takes place at the end of each pass. This can be accomplished by means of a do-while statement. The general form of a do-while statement is

do  statement  while  (expression);
The statement is executed repeatedly, as long as the expression is true. Note, however, that the statement is always executed at least once, since the test for repetition does not take place until the end of the first pass through the loop. The statement can be either simple or compound, and should, of course, include some feature that eventually alters the value of the expression.

The program listed below is a marginally improved version of the previous program (iteration.c) which uses a do-while loop to test for convergence at the end (as opposed to the beginning) of each iteration loop.

/* iteration1.c */
/* 
   Program to solve algebraic equation

    5      2 
   x  + a x  - b = 0

   by iteration. Easily shown that equation must have at least
   one real root. Coefficients a and b are supplied by user.

   Iteration scheme is as follows:

                    2  0.2
   x    =  ( b - a x  )
    n+1             n

   where x_n is nth iteration. User must supply initial guess for x.
   Iteration continues until relative change in x per iteration is 
   less than eps (user supplied) or until number of iterations exceeds 
   NITER. Program aborts if (b - a x*x) becomes negative. 
*/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/* Set max. allowable no. of iterations */
#define NITER 30  

int main() 
{
  double a, b, eps, x, x0, dx, d;
  int count = 0;

  /* Read input data */
  printf("\na = ");
  scanf("%lf", &a);
  printf("b = ");
  scanf("%lf", &b);
  printf("eps = ");
  scanf("%lf", &eps);

  /* Read initial guess for x */
  printf("\nInitial guess for x = ");  
  scanf("%lf", &x);
  x0 = x;

  do   // Start iteration loop
   {
    /* Check for too many iterations */
    ++count;
    if (count > NITER) 
     {
      printf("\nError: no convergence\n");
      exit(1);
     }

    /* Reject complex roots */
    d = b - a * x * x;
    if (d < 0.)     
     {
      printf("Error: complex roots - try another initial guess\n");
      exit(1);
     }

    /* Perform iteration */
    x = pow(d, 0.2);
    dx = fabs( (x - x0) / x );
    x0 = x;

    /* Output data on iteration */
    printf("Iter = %3d   x = %8.4f   dx = %12.3e\n", count, x, dx);

   } while (dx > eps);  // Test for convergence

  return 0;
}
The output from the above program is essentially identical to that from the program iteration.c.

The while and do-while statements are particularly well suited to looping situations in which the number of passes through the loop is not known in advance. Conversely, situations in which the number of passes through the loop is known in advance are often best dealt with using a for statement. The general form of a for statement is

for  (expression 1;  expression 2;  expression 3)  statement
where expression1 is used to initialize some parameter (called an index) that controls the looping action, expression2 represents a condition that must be true for the loop to continue execution, and expression3 is used to alter the value of the parameter initially assigned by expression1. When a for statement is executed, expression2 is evaluated and tested at the beginning of each pass through the loop, whereas expression3 is evaluated at the end of each pass.

The program listed below uses a for statement to evaluate the factorial of a non-negative integer.

/* factorial.c */
/* 
   Program to evaluate factorial of non-negative
   integer n supplied by user.
*/

#include <stdio.h>
#include <stdlib.h>

int main()
{
  int n, count;
  double fact = 1.;
  
  /* Read in value of n */
  printf("\nn = ");
  scanf("%d", &n);

  /* Reject negative value of n */
  if (n < 0) 
   {
    printf("\nError: factorial of negative integer not defined\n");
    exit(1);
   }

  /* Calculate factorial */
  for (count = n; count > 0; --count) fact *= (double) count;

  /* Output result */
  printf("\nn = %5d    Factorial(n) = %12.3e\n", n, fact);

  return 0;
}
The typical output from the above program is shown below:
n = 6
 
n =     6    Factorial(n) =    7.200e+02 
%

The statements which occur within if-else, while, do-while, or for statements can themselves be control statements, giving rise to the possibility of nested if-else statements, conditionally executed loops, nested loops, etc. When dealing with nested control statements, it is vital to adhere religiously to the syntax rules described above, in order to avoid confusion.


next up previous
Next: Functions Up: Scientific programming in C Previous: Structure of a C
Richard Fitzpatrick 2006-03-29