Talking to the terminal

Real-World Analogy

Think of your program as someone sitting at a desk. The terminal screen in front of them is standard output — where they write notes for others to see. The keyboard is standard input — where they receive messages coming in. printf is the act of writing on the screen. scanf is the act of reading what's been typed on the keyboard. Every C program, from "Hello World" to operating systems, uses these two channels.

In Python you used print("Hello") to display text and input("Name: ") to read from the keyboard. In C, the equivalents are printf("Hello\n") and scanf("%s", name). The concept is identical — but C requires one extra detail that Python hides from you: you must tell C exactly what type of data you are printing or reading.

This is done through format specifiers — short codes inside the format string that begin with %. When you write printf("%d", age), the %d is a placeholder that says "insert an integer here." When you write scanf("%f", &price), the %f says "read a float from the keyboard and store it."

Why the & in scanf? This is the most common beginner confusion. When you pass a variable to scanf, you are not giving it the value of the variable — you are giving it the memory address where scanf should write the result. The & operator means "give me the address of." You will understand this deeply when you study pointers in Topic 08. For now: always put & before variable names in scanf (except strings/arrays, which are already addresses).

What about getchar and putchar? These are the most basic I/O functions — they handle one character at a time. getchar() reads the next character from stdin and returns it as an int. putchar(c) writes one character to stdout. They are rarely used alone but they power all of C's higher-level I/O under the hood.

Python (what you know)
# Output — Python
print("Hello")
print("Age:", 25)
print(f"Score: {score:.2f}")

# Input — Python
name = input("Enter name: ")
age = int(input("Enter age: "))
C (what you are learning)
/* Output — C */
printf("Hello\n");
printf("Age: %d\n", 25);
printf("Score: %.2f\n", score);

/* Input — C */
char name[50];
int age;
scanf("%s", name);   /* no & for arrays */
scanf("%d", &age);   /* & required here */
C does NOT add a newline automatically

Python's print() adds a newline at the end automatically. C's printf() does not. You must always write \n yourself when you want to move to the next line. Forgetting \n means your next output will appear on the same line — a very common beginner mistake.

Where does this live in your code? All I/O functions come from the standard I/O library, which you import at the top of every C file with #include <stdio.h>. Without this line, the compiler will not know what printf and scanf are. The .h stands for "header file" — it declares function signatures so the compiler can check your usage.

The man page is your best friend. When you want to know exactly how a function works — what it returns, what format strings it accepts, what errors it can produce — consult the manual: run man 3 printf in the terminal. Section 3 is C library calls. This is a habit every professional C programmer has.

You've got the mental model!

printf = write to screen, you control the format. scanf = read from keyboard, you specify the type AND give the address with &. Include <stdio.h> at the top. That's the whole picture — everything else is details about format specifiers, which you'll memorize quickly with practice.

printf, scanf, and format specifiers

Here is the annotated signature and usage of both functions:

/* printf — print formatted output to stdout */
int printf(const char *format, ...);
 ^   ^        ^               ^
 |   |        |               └── zero or more extra arguments (the values to print)
 |   |        └────────────────── format string: text + %specifiers
 |   └─────────────────────────── function name
 └─────────────────────────────── returns int: number of characters printed

printf("%d %f\n", 10, 10.5);
         ^  ^   ^         ^ (one extra argument per % specifier)

/* scanf — read formatted input from stdin */
int scanf(const char *format, ...);
 ^   ^                        ^
 |   |                        └── pointers to variables — use & for non-arrays
 |   └──────────────────────────── function name
 └──────────────────────────────── returns int: number of items successfully read

int x; float f;
scanf("%d %f", &x, &f);
              ^   ^   ^ ampersand gives scanf the ADDRESS to write into
Key rule: The number of % specifiers in the format string must exactly match the number of extra arguments. One %d → one integer argument. Two specifiers %d %f → two arguments. Mismatch causes undefined behavior.

Format Specifiers — Complete Reference

Specifier Type it reads/prints Example (printf) Output
%d Signed integer (int) printf("%d", 42); 42
%u Unsigned integer (unsigned int) printf("%u", 42u); 42
%f Float or double (decimal) printf("%f", 3.14); 3.140000
%.2f Float/double with 2 decimal places printf("%.2f", 3.14159); 3.14
%g Float/double, shorter representation printf("%g", 3.14); 3.14
%e Float/double in scientific notation printf("%e", 3.14); 3.140000e+00
%c Single character (char) printf("%c", 'A'); A
%s String (char array ending in \0) printf("%s", "hi"); hi
%p Pointer address (hexadecimal) printf("%p", &x); 0x7ffd12ab
%x Unsigned integer as hexadecimal printf("%x", 255); ff
%ld Long integer (long) printf("%ld", 1234567L); 1234567
%lf Double for scanf (use %f for printf) scanf("%lf", &d); reads double
%zu size_t (result of sizeof) printf("%zu", sizeof(int)); 4
%% Literal percent sign printf("100%%"); 100%
%f vs %lf — the scanf trap

For printf: use %f for both float and double — C automatically promotes float to double when passed to printf.
For scanf: use %f for float and %lf (lowercase L + f) for double. Getting this wrong silently corrupts data. This is a very common exam trick.

Escape Sequences

SequenceMeaningUse case
\nNewline (move to next line)End of every output line
\tHorizontal tabAligned columns
\rCarriage return (go to line start)Overwrite current line
\\Literal backslashPrint a backslash character
\"Literal double quotePrint " inside a string
\0Null character (ASCII 0)String terminator in C

Width and Precision Formatting

/* Width: minimum field width */
printf("%8d", 42);        // "      42"  (right-aligned, padded to 8 chars)
printf("%-8d", 42);       // "42      "  (left-aligned with -)
printf("%08d", 42);       // "00000042"  (zero-padded)

/* Precision: decimal places for floats */
printf("%.2f", 3.14159);  // "3.14"
printf("%.0f", 3.7);     // "4"   (rounds)
printf("%8.2f", 3.14);   // "    3.14"  (width 8, 2 decimal places)
Pattern: %[flags][width][.precision]specifier — flags are optional (- left-align, 0 zero-pad), width and precision are optional integers.

getchar and putchar

/* getchar — read one character from stdin, returns int */
int getchar(void);
 ^   returns EOF (-1) when no more input (Ctrl+D on Linux)

/* putchar — write one character to stdout */
int putchar(int c);

/* Classic pattern: echo all input back to output */
int c;
while ((c = getchar()) != EOF) {
    putchar(c);
}
/* Why int not char? EOF is -1, which doesn't fit in a char on some systems */
Important: Always store getchar()'s return value in an int, not a char. On systems where char is unsigned, comparing a char to EOF (-1) can never be true — the loop runs forever. This is a classic C bug.

Complete programs you can compile and run

Example 1 — printf with multiple types and format options Week 1 Lecture
#include <stdio.h>   /* Always needed for printf/scanf */

int main(void) {
    int    age    = 21;
    double height = 1.75;
    char   grade  = 'A';
    char   name[] = "Alice";

    /* %d = integer, %f = float/double, %c = char, %s = string */
    printf("Name:   %s\n",   name);
    printf("Age:    %d\n",   age);
    printf("Height: %.2f\n", height);  /* .2 = 2 decimal places */
    printf("Grade:  %c\n",   grade);

    /* Combining multiple values in one printf call */
    printf("%s is %d years old, %.1fm tall, grade %c\n",
           name, age, height, grade);

    /* Width formatting: right-align numbers in columns */
    printf("%10s | %5d | %8.2f\n", "Alice", 21, 1.75);
    printf("%10s | %5d | %8.2f\n", "Bob",   19, 1.82);

    return 0;
}
Output
Name: Alice
Age: 21
Height: 1.75
Grade: A
Alice is 21 years old, 1.8m tall, grade A
Alice | 21 | 1.75
Bob | 19 | 1.82
Example 2 — Reading input with scanf: Fahrenheit to Celsius converter Week 1 Lecture — Page 15
#include <stdio.h>

int main(void) {
    int ftemp;   /* fahrenheit temperature entered by user */

    /* Prompt the user — printf does NOT flush newline automatically */
    printf("Please enter a Fahrenheit temperature: ");

    /* scanf reads one integer from stdin and stores it at &ftemp */
    /* &ftemp = "the address of ftemp" — scanf writes INTO that address */
    int n = scanf("%d", &ftemp);

    /* Always check scanf's return value: it returns number of items read */
    /* If the user types letters instead of a number, n will be 0 */
    if (n != 1) {
        printf("Error: please enter a whole number.\n");
        return 1;
    }

    /* Formula: C = (F - 32) * 5 / 9  */
    /* Integer division! (ftemp-32)*5/9 truncates — use doubles for exactness */
    int ctemp = (ftemp - 32) * 5 / 9;
    printf("%d Fahrenheit is %d Celsius\n", ftemp, ctemp);

    return 0;
}
Sample run (user types 100)
Please enter a Fahrenheit temperature: 100
100 Fahrenheit is 37 Celsius
Example 3 — Reading multiple values and using getchar/putchar Week 1 Lecture + Tutorial
#include <stdio.h>

int main(void) {
    int   age;
    float height;
    char  grade;
    char  name[50];

    /* Read multiple values — space in format string skips whitespace */
    printf("Enter age and height: ");
    scanf("%d %f", &age, &height);

    /* After scanf reads numbers, a newline '\n' remains in the buffer.
       Read and discard it before reading a character with %c */
    scanf(" %c", &grade);   /* leading space in " %c" skips whitespace */

    printf("Age: %d, Height: %.1f, Grade: %c\n", age, height, grade);

    /* --- getchar / putchar demo --- */
    printf("\nEcho program (type text, press Ctrl+D to stop):\n");
    int c;
    while ((c = getchar()) != EOF) {   /* EOF = -1, returned on end-of-input */
        putchar(c);                     /* write the same character to stdout */
    }
    printf("\n(End of input reached)\n");

    return 0;
}
Sample run
Enter age and height: 20 1.75
A
Age: 20, Height: 1.8, Grade: A

Echo program (type text, press Ctrl+D to stop):
hello
hello
(End of input reached)

Practice problems with solutions

P1 — What does this print? Predict the output. Week 2 Tutorial

Without running the code, predict exactly what each printf statement outputs.

#include <stdio.h>
int main(void) {
    int x = 42;
    float f = 3.14159f;
    char c = 'Z';

    printf("%d\n", x);
    printf("%f\n", f);
    printf("%.2f\n", f);
    printf("%c has ASCII value %d\n", c, c);
    printf("%10d|\n", x);
    printf("%-10d|\n", x);
    printf("%05d\n", x);
    return 0;
}
42
3.141590
3.14
Z has ASCII value 90
        42|
42        |
00042
Line by line: %d prints 42. %f with no precision prints 6 decimal places by default → 3.141590. %.2f rounds to 2 places → 3.14. %c prints the character 'Z', %d on the same char prints its ASCII code (90). %10d right-aligns in a field of 10. %-10d left-aligns. %05d zero-pads to 5 digits.
P2 — Spot the bug: three scanf errors Week 1 Lecture + Tutorial

The code below has three bugs — all related to scanf. Find and fix each one.

#include <stdio.h>
int main(void) {
    int age;
    double salary;
    char name[50];

    scanf("%d", age);          /* Bug 1 */
    scanf("%f", &salary);      /* Bug 2 */
    scanf("%s", &name);        /* Bug 3 */

    printf("Age: %d, Salary: %.2f, Name: %s\n", age, salary, name);
    return 0;
}
#include <stdio.h>
int main(void) {
    int age;
    double salary;
    char name[50];

    scanf("%d", &age);         /* Fix 1: need & — age is not a pointer */
    scanf("%lf", &salary);     /* Fix 2: double needs %lf in scanf, not %f */
    scanf("%s", name);          /* Fix 3: name is already a pointer (array) — no & */

    printf("Age: %d, Salary: %.2f, Name: %s\n", age, salary, name);
    return 0;
}
Bug 1: Missing & before age. Without &, you pass the value of age (garbage, since uninitialised) as an address — this causes a segfault or undefined behavior.
Bug 2: Using %f for a double in scanf. In scanf, %f reads into a float (4 bytes) but salary is a double (8 bytes) — this corrupts memory. Use %lf for double in scanf.
Bug 3: Adding & before name. An array name already decays to a pointer to its first element — adding & gives a pointer-to-array, which has the wrong type. Just write name.
P3 — Write a precise temperature converter Week 1 Lecture — extended

The lecture's Fahrenheit converter uses integer division, which truncates. Rewrite it to use double for exact results, accepting decimal Fahrenheit input and printing to 2 decimal places. Expected: 98.6°F → 37.00°C.

#include <stdio.h>

int main(void) {
    double ftemp;
    printf("Enter Fahrenheit temperature: ");

    if (scanf("%lf", &ftemp) != 1) {   /* %lf for double in scanf */
        printf("Invalid input.\n");
        return 1;
    }

    double ctemp = (ftemp - 32.0) * 5.0 / 9.0;  /* use .0 literals to avoid int division */
    printf("%.2f F = %.2f C\n", ftemp, ctemp);

    return 0;
}
Key changes: Use double instead of int. Read with %lf (scanf's double specifier). Use 32.0, 5.0, 9.0 (or cast) so arithmetic stays floating-point — if all literals are int, C does integer arithmetic and truncates before converting. Print with %.2f for 2 decimal places.
P4 — Count vowels using getchar Week 2 Tutorial

Write a program that reads characters one by one from stdin (using getchar) and counts how many vowels (a, e, i, o, u — lowercase only) appear before EOF. Print the count at the end.

#include <stdio.h>

int main(void) {
    int c;
    int vowel_count = 0;

    while ((c = getchar()) != EOF) {
        /* Check if c is a lowercase vowel */
        if (c == 'a' || c == 'e' || c == 'i' ||
            c == 'o' || c == 'u') {
            vowel_count++;
        }
    }

    printf("Vowel count: %d\n", vowel_count);
    return 0;
}
Important: c must be int, not char. EOF is -1 and does not fit in a char on all platforms. The assignment c = getchar() inside the condition is intentional — it reads, assigns, and checks all in one step. The outer parentheses are required: (c = getchar()) forces evaluation before != EOF.
P5 — What is the return value of scanf and why does it matter? Tutorial + man 3 scanf

Consider this code. What happens when the user types "hello" instead of a number? What does scanf return? How do you make the program handle this gracefully?

#include <stdio.h>
int main(void) {
    int x;
    scanf("%d", &x);
    printf("You entered: %d\n", x);
    return 0;
}
#include <stdio.h>
int main(void) {
    int x;
    int result = scanf("%d", &x);

    /* scanf returns: number of items successfully matched and assigned.
       If user types "hello", scanf cannot parse an integer.
       result == 0 (matched zero items), x remains UNINITIALISED. */
    if (result == 0) {
        printf("Error: expected a number.\n");
        return 1;
    }
    /* result == EOF (-1) if stdin was closed before any input */
    if (result == EOF) {
        printf("Error: end of input.\n");
        return 1;
    }

    printf("You entered: %d\n", x);
    return 0;
}
What scanf returns: The number of items it successfully read and stored. For scanf("%d", &x), success = 1, failure = 0, end-of-file = EOF (-1). If scanf fails, x is left uninitialised — reading it is undefined behavior. Always check scanf's return value in production code. The man page (man 3 scanf) gives the full specification.

Key concepts to memorize

Card 1 of 10
Question — click to flip
Answer
Click card to flip • Use buttons to navigate

Test your understanding

Topic 02 Quiz — I/O Score: 0 / 6
1
What format specifier should you use to print a double with exactly 3 decimal places?LO1
multiple choice
2
True or False: When reading a double with scanf, the correct format specifier is %lf (not %f).LO1
true / false
3
What does getchar() return when it reaches end-of-file?LO1
multiple choice
4
Fill in the blank: printf("___", 255); to print 255 in hexadecimal (answer: the format specifier only, e.g. %d).LO1
fill in the blank
5
Spot the bug: what is wrong with this code?LO1
char c;
c = getchar();
if (c == EOF) {
    printf("End of file\n");
}
spot the bug — multiple choice
6
Which header file must you #include to use printf and scanf?LO8
multiple choice
0/6
Quiz complete!