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
scanfand 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.
stdin
standard input
program ← input
Usually the keyboard
stdout
standard output
program → output
Normal results
stderr
standard error
program → errors
Diagnostics
#include <stdio.h>
int main(void) {
printf("Normal result\n"); // stdout
fprintf(stderr, "Helpful diagnostic\n"); // stderr
return 0;
}Tip
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.
| Conversion | Expected argument | Example |
|---|---|---|
%d / %i | int | printf("%d", count) |
%u | unsigned int | printf("%u", flags) |
%f | double | printf("%.2f", price) |
%c | int representing a character | printf("%c", grade) |
%s | pointer to a null-terminated string | printf("%s", name) |
%zu | size_t | printf("%zu", sizeof value) |
%p | void pointer | printf("%p", (void *) ptr) |
#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
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.”
#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.
#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
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.
#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.
#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
%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.