K&R C Exercise 1-8: Count Blanks, Tabs, and Newlines

Exercise 1-8. Write a program to count blanks, tabs, and newlines.

Approach

The solution maintains three independent counters and reads one character at a time using the canonical getchar() loop. The key design decision is using three separate if statements rather than else if. In this case a character can only ever be one thing — a space, a tab, or a newline — so else if would work, but the three-if form makes each test independent and explicit, which is the style K&R use here to introduce the pattern cleanly.

One important detail: c must be declared as int, not char. The value EOF is typically -1, and on platforms where char is unsigned, a char can never hold -1, making the loop condition always false. Declaring c as int avoids this portability trap — it is the same reason K&R use int throughout Chapter 1.

Solution

/* K&R Exercise 1-8 — count blanks, tabs, and newlines */
/* Compile: gcc -ansi -Wall ex1-8.c -o ex1-8 */
#include <stdio.h>

int main(void)
{
    int c, blanks, tabs, newlines;

    blanks = tabs = newlines = 0;
    while ((c = getchar()) != EOF) {
        if (c == ' ')
            ++blanks;
        if (c == '\t')
            ++tabs;
        if (c == '\n')
            ++newlines;
    }
    printf("blanks: %d\ntabs: %d\nnewlines: %d\n", blanks, tabs, newlines);
    return 0;
}

Line-by-Line Walkthrough

Chain assignmentblanks = tabs = newlines = 0 initialises all three counters in one statement. Assignment is right-associative in C: it evaluates as blanks = (tabs = (newlines = 0)).

The input loopwhile ((c = getchar()) != EOF) is the canonical K&R idiom for reading until end of file. The inner parentheses around c = getchar() are required: without them, = has lower precedence than !=, and the compiler would compare the return value of getchar() against EOF first, then assign the boolean result (0 or 1) to c. This idiom was explained in detail in Exercise 1-6.

Three separate if statements — each character is tested against all three conditions on every pass through the loop. This differs from if / else if / else: with else if, once one condition matches, the others are skipped. Here it makes no practical difference because no character can simultaneously be a blank, a tab, and a newline — but the pattern keeps each test independent, which is useful when conditions are not mutually exclusive in more general programs.

Escape sequences'\t' is the tab character (ASCII 9) and '\n' is the newline character (ASCII 10). Omitting the backslash — comparing against 't' or 'n' — would count the literal letters t and n, a silent bug that produces wrong results.

Compile and Run

gcc -ansi -Wall ex1-8.c -o ex1-8
./ex1-8

The program reads from standard input and waits for you to type. When you are done, signal end-of-file: press Ctrl+D on Linux/macOS or Ctrl+Z then Enter on Windows.

You can also pipe input directly so you do not have to type interactively:

echo "hello world" | ./ex1-8
printf "one\ttwo\tthree\nfour five\n" | ./ex1-8

Sample Runs

Run 1 — simple sentence with a trailing newline

$ echo "hello world" | ./ex1-8
blanks: 1
tabs: 0
newlines: 1

echo appends a newline automatically, so there is one newline even though you only typed one line.

Run 2 — tabs and multiple lines

$ printf "one\ttwo\tthree\nfour five six\n" | ./ex1-8
blanks: 2
tabs: 2
newlines: 2

Two tab-separated words on the first line give 2 tabs; two spaces on the second line give 2 blanks; each \n gives one newline.

Run 3 — interactive session

$ ./ex1-8
hello   world
[Tab key]   C programming
^D
blanks: 4
tabs: 1
newlines: 2

After pressing Ctrl+D the program prints the totals and exits.

What This Exercise Teaches

  • Character-level I/Ogetchar() reads one character at a time, giving complete control over processing; this is the foundation of most text-processing programs in C.
  • The int c ruleEOF is an integer sentinel outside the char range; always use int for variables that hold getchar() return values.
  • Escape sequences as character literals'\t' and '\n' are single characters, each fitting in an int; the backslash is part of the literal, not a separate character.
  • Chain assignmenta = b = c = 0 is idiomatic C for initialising multiple variables to the same value in one statement.
  • Independent vs. mutually exclusive conditions — understanding when to use if / if / if vs. if / else if / else is a foundational control-flow decision.

Set Up Your C Environment

To compile and run this program you need GCC installed. If you have not set up C on your machine yet:

← Exercise 1-7  | 
Chapter 1 Solutions  | 
Exercise 1-9 →

Book:

The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>