Basic Input and Output

Most useful programs require some data to be provided to them for processing (input), and will produce a further set of data as a result of that processing (output). C carries out input and output operations via streams that allow it to communicate with input and output devices. The term stream refers to the sequential flow of data to an output device or from an input device.

There are two types of stream - text and binary. For the moment, we are chiefly concerned with text streams. Text streams are composed of lines of text, each with zero or more characters, terminated by a newline character. Text streams may only contain printable characters, the tab character, and the newline character. When a program runs there are three streams available - standard input (stdin), standard output (stdout), and standard error (stderr - we will look at this stream elsewhere). The standard input device is usually the keyboard, while the standard output device is usually the screen.

We have already used the printf() function several times to print information to the screen. We will shortly be looking at using the scanf() function to get input from the keyboard. The printf() and scanf() functions are part of a library of input and output functions known as the standard input/output library. Any program that uses any of these functions must use the #include preprocessor directive to include this library file as follows:

# include <stdio.h>

The most basic form of input and output operation is to read a single character from the keyboard and display it on the screen. The following short program demonstrates the use of the getchar() function, which as its name suggests reads a character from the standard input device:

// example program 1

# include <stdio.h>

void main()
{
  char str[1];
  char c;

  printf("Enter a single character: ");
  c = getchar();  // get a character from the keyboard
  printf("\n\nThe character you entered is: %c", c);
  fflush(stdin);  // clear the input buffer
  printf("\n\nPress ENTER to continue . . . ");
  gets(str);
}


The output from example program 1

The output from example program 1


The complementary function to getchar() is putchar(), which as its name suggests writes a single character to the standard output device. The following short program demonstrates the use of the putchar() function:

// Example program 2

# include <stdio.h>

void main()
{
  char str[1];
  char c;

  printf("Enter a single character: ");
  c = getchar();  // get a character from the keyboard
  printf("\n\nThe character you entered is: ");
  putchar(c);     // output the character
  fflush(stdin);  // clear the input buffer
  printf("\n\nPress ENTER to continue . . . ");
  gets(str);
}


The output from example program 2

The output from example program 2


Note that the program does exactly the same thing as the previous example. It simply does it in a different way (i.e. by using the putchar() function to output the character).

When dealing with text input, it is more often the case that the user will be typing in several characters at a time. In other words, they will be entering a string of characters rather than just a single character. The gets() function accepts a string of characters from the standard input device, while its equivalent output function, puts(), outputs a string to the standard output device.

There is no specific string type in C, although there is in many other programming languages. A string in C is in fact an array of type char. Both gets() and puts() accept a single argument, which is the name of an array of type char.

The gets() function accepts characters from the standard input device (normally the keyboard) and stores them in the character array referenced by the variable name passed to it as a parameter. As soon as the ENTER key is pressed, the function appends a null character to the array to terminate (mark the end of) the string, and control is returned to the line of code after the one that called the gets() function.

The character array must be large enough to hold the input string - any characters typed in by the user beyond the number of characters specified for the string (minus one for the null terminating character) will simply be lost.

The short program below demonstrates the use of the gets() and puts() functions (note that we have already used the gets() function several times to capture the user pressing the ENTER key to end a program).

// Example program 3

# include <stdio.h>

void main()
{
  char str[101];

  printf("Enter a word or phrase up to 100 characters:\n\n");
  gets(str);    // get the string from the keyboard
  printf("\n\nThe string you entered is:\n\n");
  puts(str);    // output the string to the screen
  fflush(stdin);  // clear the input buffer
  printf("\n\nPress ENTER to continue . . . ");
  gets(str);
}


The output from example program 3

The output from example program 3


The gets() function is OK for inputting strings of a known maximum length, but its use is generally discouraged because its use can lead to buffer overflows, and as such represents a potential security problem (I won't go into details here). A function that gives us more control over the format of the input data, and which is also declared in the stdio.h header file, is scanf(). The scanf() function has a similar syntax to the printf() function that we have already used, and has the general format:

scanf(char *format, argument, argument, ...);

The format string consists of one or more conversion specifiers that determine what type of data is to be read from the keyboard, and is followed by a comma-separated list of arguments, each of which is the address of a variable. The following short program illustrates how scanf() could be used:

// Example program 4

# include <stdio.h>
# include <conio.h>

void main()
{
  int i;
  float x;

  printf("What are your favorite numbers?\n\n");
  printf("(Enter an integer and a float.)\n\n");
  scanf("%d%f", &i, &x);
  printf("\nYour favorite numbers are %d and %f!\n", i, x);
  printf("\n\nPress any key to continue.");
  getch();
}


The output from example program 4

The output from example program 4


The format string "%d%f" specifies that we expect an integer and a float to be entered. The input values will be stored at &i and &x respectively. The type for each argument must correspond to the elements specified in the format string. We have seen the same kind of thing used with the printf() function to specify the type of each variable (e.g. integer, float, string etc.) passed to the function as an argument.

The conversion specifier consists of the percent sign (%) followed by a type specifier that indicates the data type (e.g. f for float). There may optionally be an additional character (or flag) between the percent sign and the type specifier, as well as a number for specifying field width or precision. Some of the commonly used format identifiers are listed in the table below.



Common Format Specifiers
SpecifierMeaning
%ccharacter
%ddecimal signed integer
%ffloating point number
%idecimal signed integer
%ldlong decimal integer
%ooctal integer
%sstring
%uunsigned decimal integer
%xhexadecimal integer (using lower case a-f)
%Xhexadecimal integer (using upper case A-F)


A single format specifier can have up to six components, as shown in the table below. If used, they must appear in the order shown.



Format Specifier Components
%flagsmin field widthperiodprecision, max field widthType specifier
requiredoptionaloptionaloptionaloptionalrequired


One or more flags can be used immediately following the % symbol. Their purpose is to modify the default behaviour of the format identifiers that follow. For example, a minus sign causes the item to be displayed left-justified, while a zero causes the field to be padded with zeros rather than blanks. The exact meaning of minimum field width, maximum field width and precision depend on the data type being represented, as demonstrated by the two examples below.

  1. %8.2f - this specifies a floating point value, with a total field width of eight characters, and with the last two characters displaying the decimal part of the number.
  2. %4.8s - this specifies a string value, with a minimum width of four characters, and a maximum width of eight characters (if the string exceeds eight characters, it will be truncated).

As well as format specifiers like those described above, the conversion string passed to the printf() function may contain string literals and escape characters. The number and type of arguments passed to both printf() and scanf() can vary, but must be matched by the number of format specifiers in the conversion string. The somewhat longer program below reads integer values from the keyboard, calculates the average of the values entered, and displays the smallest value entered, the largest value entered, the sum of the values entered, and the average value as calculated.

// Example program 5

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

void main()
{
  float temp = 0.0, total = 0.0, average = 0.0;
  int sample = 255, sample_no = 0;
  int min = 255, min_sample_no = 0;
  int max = 0, max_sample_no = 0;

  while(sample != 0)
  {
    printf("\n\nEnter a number between 1 and 254");
    printf(" (or 0 to terminate data entry): ");
    scanf("%f", &temp );
    sample = (int) temp;
    if ((sample > 0) && (sample < 255))
    {
      sample_no += 1;
      total += sample;
      if((sample < min) && (sample > 0))
      {
        min = sample;
        min_sample_no = sample_no;
      }

      if((sample > max) && (sample < 255))
      {
        max = sample;
        max_sample_no = sample_no;
      }
    }
    else if(sample != 0)
    {
      printf("\nYou entered an invalid value. ");
      printf("Press any key to continue . . . ");
      getch();
    }
  }
  if(total == 0.0)
  {
    printf("\n\nYou did not enter any values!\n\n");
    printf("Press any key to continue . . . ");
    getch();
    exit(0);
  }
  average = total / sample_no;
  printf("\n\nNumber of entries read: %i\n\n", sample_no);
  printf("Sum of values entered: %0.0f\n\n", total);
  printf("Largest number entered: %i (sample number %i)\n\n", max, max_sample_no);
  printf("Smallest number entered: %i (sample number %i)\n\n", min, min_sample_no);
  printf("Average value: %6.2f\n\n", average);
  printf("Press any key to continue . . . ");
  getch();
}


The output from example program 5

The output from example program 5


The sscanf() function is closely related to the scanf() function, and is also declared in stdio.h. It allows you to get data from a buffer (a string variable, for example). The general format for sscanf() is:

char sscanf(const char *buff, const char *format, ...);

The first argument is the address of the buffer from which the sscanf() function obtains its input. This is followed by a format string that determines what type of data is to be read from the buffer, then the addresses of the variables (separated by commas) to which the values read are to be assigned. The following example illustrates how sscanf() can be used to extract substrings from a string.

// Example program 6

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

void main()
{
  char input_string[30] = "   47.0000  10:00:56  ";
  char temperature_string[8];
  char time_string[9];
  int temperature;

  sscanf(input_string, "%7s %8s", temperature_string, time_string);
  temperature = atoi(temperature_string);
  printf("\nTemperature is: %8.4f\n", (float) temperature);
  printf("\nThe time recorded was: %s\n", time_string);
  printf("\n\nPress any key to exit . . . ");
  getch();
}


The output from example program 6

The output from example program 6