Exercise 1-12. Write a program that prints its input one word per line.
Approach
A word is any contiguous run of non-whitespace characters; spaces, tabs, and newlines are all separators. The challenge is not reading words — it is knowing when to emit the newline that separates them in the output.
The solution borrows the two-state machine K&R introduce for their word-count program on page 20: a variable called state tracks whether the program is currently inside a word (IN) or outside one (OUT). The newline is printed exactly once — when the state transitions from IN to OUT on the first whitespace character after a word ends. Every subsequent whitespace character is ignored because the state is already OUT. This single rule handles any combination of spaces, tabs, and newlines without producing blank lines.
Solution
/* K&R Exercise 1-12 — print input one word per line */
/* Compile: gcc -ansi -Wall ex1-12.c -o ex1-12 */
#include <stdio.h>
#define IN 1 /* currently inside a word */
#define OUT 0 /* currently outside a word */
int main(void)
{
int c, state;
state = OUT;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t') {
if (state == IN) /* first whitespace after a word: end it */
putchar('\n');
state = OUT;
} else {
putchar(c); /* non-whitespace: echo and stay/enter IN */
state = IN;
}
}
return 0;
}
How the State Machine Works
Step through the input "hello world" character by character:
| Character | state before | Action | state after |
|---|---|---|---|
h |
OUT | print h |
IN |
e |
IN | print e |
IN |
l |
IN | print l |
IN |
l |
IN | print l |
IN |
o |
IN | print o |
IN |
' ' (1st space) |
IN | print \n — word ended |
OUT |
' ' (2nd space) |
OUT | nothing | OUT |
' ' (3rd space) |
OUT | nothing | OUT |
w |
OUT | print w |
IN |
o, r, l, d |
IN | print each char | IN |
| EOF | IN | loop exits, no extra newline | — |
Three spaces produce exactly one newline in the output — not three. And because the input ends mid-word (no trailing whitespace), the loop exits without printing a spurious final newline after world.
A Common Mistake to Avoid
A naive version emits the newline when entering a word (the OUT→IN transition):
/* Wrong — produces a blank first line and misplaced newlines */
if (c != ' ' && c != '\n' && c != '\t') {
if (state == OUT)
putchar('\n'); /* newline before the word, not after */
putchar(c);
state = IN;
}
This inserts a newline before each word instead of after the previous one. On the very first word the state is OUT, so it prints a blank line before any output at all. Emitting on IN→OUT avoids both problems.
Compile and Run
gcc -ansi -Wall ex1-12.c -o ex1-12
The program reads from standard input. Pipe text directly to see the results immediately:
echo "the quick brown fox" | ./ex1-12
Or run interactively — type words and press Ctrl+D (Linux/macOS) or Ctrl+Z then Enter (Windows) to send EOF.
Sample Output
$ echo "the quick brown fox" | ./ex1-12 the quick brown fox $ printf "one\t\ttwo\nthree" | ./ex1-12 one two three $ printf " leading and trailing " | ./ex1-12 leading and trailing
The third example shows both edge cases at once: leading whitespace before the first word is silently skipped (state starts at OUT), and trailing whitespace after the last word produces no extra blank line (the IN→OUT transition fires but no new word follows).
What This Exercise Teaches
- Two-state finite automaton. Using a single integer variable to remember context — whether the program is inside or outside a token — across iterations of a loop. This pattern reappears throughout K&R and in real lexers and parsers.
- Emit output at the right transition. The newline is printed once when the state changes from
INtoOUT, not once per whitespace character. Getting that timing right eliminates both missing separators and duplicate blank lines with no extra logic. - Symbolic constants improve readability.
#define IN 1and#define OUT 0make the conditions self-documenting.if (state == IN)reads immediately;if (state == 1)does not. - Character-level I/O with
getchar()andputchar(). Processing one character at a time keeps memory usage constant regardless of input length — no buffer or array needed.
Set Up Your C Environment
To compile and run this solution, you need GCC installed. If you have not set up C on your machine yet:
- Complete C Development Environment Setup — start here if you are new to C
- Install GCC on Windows 11
- Install GCC on macOS
- Install GCC on Ubuntu/Linux
- VS Code for C Programming — recommended editor
- Best Online C Compilers — no install needed
← Exercise 1-11 |
Chapter 1 Solutions |
Exercise 1-13 →
Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie