Chapter 04 of 24

4. Input and output

A conversation with the program

Output is information your program sends to a person or another part of the system. Input is information it receives. The simplest learning sequence is to show a prompt, read what the person typed, check that it is usable, and then show a result.

  1. Use a function such as puts or printf to display a message.
  2. Read input deliberately; input can be missing, too long, or in the wrong format.
  3. Only convert or calculate with the input after checking that the read succeeded.

For a first exercise, ask for a whole number and print its double. Then try letters or an empty line and decide how the program should respond instead of assuming every input is valid.

Use <stdio.h> for basic console input and output.

#include <stdio.h>

int main(void)
{
    int number;
    printf("Enter an integer: ");

    if (scanf("%d", &number) != 1) {
        fputs("That was not a valid integer.\n", stderr);
        return 1;
    }

    printf("You entered %d\n", number);
    return 0;
}

scanf needs the address of an object for most conversions, hence &number.
Always check its return value: it tells you how many input items were
successfully read. scanf is easy to misuse for complex input, especially
strings. Later, prefer reading a whole line with fgets and parsing it with
functions such as strtol.

Output functions include printf, puts, putchar, and fprintf. The
standard error stream is stderr, useful for diagnostics.

Reading input deliberately

Input is data from outside the program, so it may be missing, malformed, too
long, or outside the range you can represent. A good input routine checks all
of these rather than assuming a person will always type the expected value.
The scanf example checks whether one integer was converted. For simple
teaching examples that is useful, but a failed conversion can leave unwanted
characters waiting in the input stream.

For line-oriented input, fgets reads no more than the supplied buffer size
minus one and appends a null terminator when it succeeds. A line may be
truncated if the buffer is too small; detect this when it matters. To convert
a line to an integer, strtol is generally easier to validate than atoi:
it can report where parsing stopped and whether a range error occurred. Check
that at least one digit was consumed, reject unexpected trailing characters
according to your input rules, and check errno for range errors.

#include <stdio.h>

int main(void)
{
    char line[128];

    fputs("Enter a line: ", stdout);
    if (fgets(line, sizeof line, stdin) == NULL) {
        fputs("No input was read.\n", stderr);
        return 1;
    }

    printf("You entered: %s", line);
    return 0;
}

This demonstrates a bounded read and checking for end-of-file or read
failure. A production input routine should also detect whether the line was
truncated and decide what to do with the remaining characters.

Do not pass untrusted text as the format string to printf. Use a fixed
format, such as printf("%s", text), or use fputs(text, stdout). A format
string controls how arguments are interpreted; it is not just decoration.
Use \n for a newline and %% when a percent sign should be printed by
printf.

Worked example: validate an integer line

The following function accepts leading and trailing whitespace, requires at
least one integer digit, rejects extra non-whitespace text, and checks that
the result fits in int. It leaves the output unchanged when parsing fails:

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>

bool parse_int_line(const char *text, int *output)
{
    if (text == NULL || output == NULL) {
        return false;
    }

    errno = 0;
    char *end = NULL;
    long parsed = strtol(text, &end, 10);

    if (text == end || errno == ERANGE ||
        parsed < INT_MIN || parsed > INT_MAX) {
        return false;
    }

    while (*end != '\0' && isspace((unsigned char)*end)) {
        ++end;
    }

    if (*end != '\0') {
        return false;
    }

    *output = (int)parsed;
    return true;
}

strtol accepts leading whitespace and an optional sign. text == end
detects that no digits were consumed. The loop allows only trailing
whitespace; a string such as "12abc" is rejected. The conversion to int
occurs only after its range is checked. A caller should first use fgets
and separately decide what to do if the line was too long or input ended.

Checkpoint: For each input path, decide what the program does on valid
input, invalid input, and end-of-file. Practice reading one line and reporting
whether it is a valid integer.

Exercises: read two numbers and print their sum; reject invalid input;
print a small multiplication table.


Suggested topics

0% read