Skip to content
Physics and Astronomy
Home Our Teaching Resources C programming Appendix: A better loop
Back to top
On this page
Contents

The occasional appendices and optional examples in this module are for advanced material that you will not need for this module. They are intended for enthusiastic students who are interested in going further in programming for its own sake.

Appendix: A better input loop

Errors when reading data from a file

Scientific "number-crunching" programs often run as "batch jobs" in a queueing system and read their data from a file. If there's an error in the input file it can be very confusing (and hard to fix) if the program then runs (possibly for several days) and just produces the wrong answers at the end. Therefore our program should try to help our users diagnose such problems. However, in this case there is no point in havng a "try again" loop.

The general approach is simple:

1. Check for invalid or missing input

For example:

if (mass <= 0 ) {
  fprintf(stderr, "ERROR: negative mass: %g\n", mass);
  exit(1);
}

Note how we have printed out the erroneous value, the problem may be that the user has missed out a number, or put in too many and the program is reading in the wrong number and this will help them find it.

2. Have a log file

After reading in the initial data we can print out our understanding of it to the log file.

When encountering invalid data in a non-interactive program  it's usually best to print a helpful error message and quit.

A better loop when reading from standard input

A variation of the infinite for(;;) loop in the lecture allows us to improve our infinite loop to handle a non-obvious problem: what if our program isn't reading its input from a human being typing at a keyboard, but from a file on the computer or another program? This might happen if, for example, we put our program on the web.

A sesnible approach is to give the user a few attempts but after that to fail in much the same way as above:

/*
 * Demonstrate a slightly better infinite loop
 * This could be the start of a noughts and crosses program
 */
#include <stdio.h>
#include <stdlib.h>

int squareisplayed(int x, int y);

int main() {
  int x, y;

  printf("Welcome to the noughts and crosses program\n");

  for ( int tries = 1; ; ++tries ) {
    printf("Please enter the x and y coordinates in the range 1-3 ");
    scanf("%d %d", &x, &y);

    if ( x < 1 || x > 3 || y < 1 || y > 3  ) 
      printf("\nThe inputs are in the wrong range, please try again.\n\n");
    else if ( squareisplayed(x, y) )
      printf("\nSorry, the square (%i, %i) has already been played\n", x, y);
    else
      break;

    if ( tries > 5 ) {
      printf("Sorry, too many tries\n");
      exit(1);
    }
  }

  printf("Your move is: (%d, %d)\n", x, y);
  return 0;
}

Note again how missing out the test in the loop defaults to the test being assumed to be true. Note too how after the number of tries has been exceeded the program does not just carry on(!) but quits.

Putting a wrapper around the test

If it gets a little tedious to have the if ( tries > 5 ) test inside every input loop then a cleaner way is to put the test into a small wrapper function and to use that as the test in the for() loop. The nice thing about this is that our test function, which we have called toomany() can handle the exit from the program:

/*
 * Demonstrate a slightly better infinite loop
 * This could be the start of a noughts and crosses program
 */
#include <stdio.h>
#include <stdlib.h>

int squareisplayed(int x, int y);

// Check for too many attempts to input and
// exit program. Otherwise return 1.
int toomany(int tries) {
  if ( tries > 5 ) {
    printf("Sorry, too many tries\n");
    exit(1);
  }

  return 1; // So the for() loop continues
}

int main() {
  int x, y;

  printf("Welcome to the noughts and crosses program\n");

  for ( int tries = 1; toomany(tries); ++tries ) {
    printf("Please enter the x and y coordinates in the range 1-3 ");
    scanf("%d %d", &x, &y);

    if ( x < 1 || x > 3 || y < 1 || y > 3  ) 
      printf("\nThe inputs are in the wrong range, please try again.\n\n");
    else if ( squareisplayed(x, y) )
      printf("\nSorry, the square (%i, %i) has already been played\n", x, y);
    else
      break;
  }

  printf("Your move is: (%d, %d)\n", x, y);
  return 0;
}

                                                                                                                                                                                                                                                                       

Validate   Link-check © Copyright & disclaimer Privacy & cookies Share
Back to top