Phase 1 · C FoundationsModule 3~40 min read

Input, Output & Formatting

Communicate through the terminal with printf, scanf, character I/O, line input, format specifiers, and validation.

What you'll learn

Console programs become useful when they can accept data, report results, and explain failures. C exposes these operations through streams and format strings—powerful tools that require an exact match between every conversion and its argument.

By the end, you'll be able to:

  • Send normal output and diagnostics to the appropriate streams
  • Format common C types with widths, precision, and flags
  • Read values with scanf and validate its return value
  • Prefer line-based input when user input needs robust parsing

Standard streams

A stream is a sequence of characters or bytes. Every hosted C program begins with three standard streams already open: one for input and two for output.

The three standard streams

stdin

standard input

program ← input

Usually the keyboard

stdout

standard output

program → output

Normal results

stderr

standard error

program → errors

Diagnostics

streams.c
#include <stdio.h>

int main(void) {
    printf("Normal result\n");                // stdout
    fprintf(stderr, "Helpful diagnostic\n"); // stderr
    return 0;
}

Tip

Keep results on stdout and failures on stderr. Shell users can redirect them separately, and automated tools can distinguish output from diagnostics.

Formatted output with printf

The first argument to printf is a format string. Ordinary characters are copied to output; conversions beginning with % consume later arguments.

ConversionExpected argumentExample
%d / %iintprintf("%d", count)
%uunsigned intprintf("%u", flags)
%fdoubleprintf("%.2f", price)
%cint representing a characterprintf("%c", grade)
%spointer to a null-terminated stringprintf("%s", name)
%zusize_tprintf("%zu", sizeof value)
%pvoid pointerprintf("%p", (void *) ptr)
formatted_output.c
#include <stdio.h>

int main(void) {
    const char *name = "Maya";
    int lessons = 6;
    double score = 93.456;

    printf("%s completed %d lessons.\n", name, lessons);
    printf("Score: %.1f%%\n", score);
    printf("|%10s| |%-10s|\n", "right", "left");
    printf("Hex: %#x\n", 255);
    return 0;
}

The format is a type contract

Passing the wrong type for a conversion produces undefined behavior. Modern compilers can check literal format strings, so keep warnings enabled and never ignore a format warning.

Formatted input with scanf

scanf reads text and converts it according to a format. It must store each converted value somewhere, so most arguments use the address-of operator &. You will study addresses deeply in the pointer lesson; for now, read &age as “the location where age is stored.”

formatted_input.c
#include <stdio.h>

int main(void) {
    int age = 0;
    double height = 0.0;

    printf("Enter age and height: ");
    int converted = scanf("%d %lf", &age, &height);

    if (converted == 2) {
        printf("Age %d, height %.2f m\n", age, height);
    }
    return 0;
}

Note

scanf uses %f for float * but %lf fordouble *. This differs from printf, where both floating arguments arrive as double and use %f.

Validate every conversion

Input is untrusted. scanf returns the number of successful assignments, orEOF when input ends before a conversion. Check that result before using the destination variables, then validate the value's allowed range.

validated_input.c
#include <stdio.h>

int main(void) {
    int quantity = 0;

    printf("Quantity: ");
    if (scanf("%d", &quantity) != 1) {
        fprintf(stderr, "Expected a whole number.\n");
        return 1;
    }
    if (quantity < 1 || quantity > 100) {
        fprintf(stderr, "Quantity must be 1..100.\n");
        return 1;
    }

    printf("Accepted: %d\n", quantity);
    return 0;
}

Key idea

Input validation has two layers: did parsing succeed? and is the parsed value acceptable? A valid integer such as -900 may still violate your program's rules.

Character I/O

getchar reads one byte from stdin, while putcharwrites one character to stdout. The result of getchar isint, not char, so it can represent every unsigned-char value plus the special EOF marker.

characters.c
#include <stdio.h>

int main(void) {
    printf("Continue? [y/n] ");
    int answer = getchar(); // int can also represent EOF

    if (answer == EOF) {
        fprintf(stderr, "No input available.\n");
        return 1;
    }

    putchar('[');
    putchar(answer);
    putchar(']');
    putchar('\n');
    return 0;
}

Safer line input with fgets

Interactive input is often easiest to control when you first read one bounded line withfgets, then parse that stored text. This separates input from conversion and prevents one malformed token from quietly remaining in the stream.

line_input.c
#include <stdio.h>

int main(void) {
    char line[100];
    char name[40];
    int age = 0;

    printf("Enter name and age: ");
    if (fgets(line, sizeof line, stdin) == NULL) {
        fprintf(stderr, "Could not read a line.\n");
        return 1;
    }

    if (sscanf(line, "%39s %d", name, &age) != 2) {
        fprintf(stderr, "Use: name age\n");
        return 1;
    }

    printf("Hello %s — next year you will be %d.\n", name, age + 1);
    return 0;
}

Bound every text conversion

An unbounded %s can overrun its destination. In %39s, the width leaves room for the terminating null character in a 40-byte array.

Later, the strings lesson will add full names, newline removal, and robust numeric parsing with strtol. The essential pattern already stands: bounded read, checked parse, validated value.

Recap & quick check

Key takeaways

  • stdin supplies input, stdout carries normal results, and stderr carries diagnostics.
  • Every printf and scanf conversion must match the exact argument type it expects.
  • scanf reports how many assignments succeeded; check that result before using input.
  • getchar returns int so it can represent every byte value and EOF.
  • Reading a bounded line with fgets and parsing it separately is a strong interactive-input pattern.

Quick check

1. Where should an error diagnostic normally be written?

2. What should scanf("%d", &value) return after one successful conversion?

3. Why is getchar's return type int?

4. What is the safest general pattern for structured interactive input?

Your programs can now communicate safely. Next up: Module 4 — Decision Making & Control Flow, where input begins changing program behavior.