Exercise 1-13. Write a program to print a histogram of the lengths of words in its input. It is easy to draw the histogram with the bars horizontal; a vertical orientation is more challenging.
Approach
The program breaks into two clear phases: counting word lengths, then rendering the histogram. Counting reuses the IN/OUT state machine introduced earlier in Chapter 1 — track whether the current character is inside or outside a word, and when you cross from IN to OUT, record how long the word was. Word lengths accumulate in an array: wlen[len]++ means “one more word of length len.” The array index is the length; no sorting or searching is needed.
The horizontal histogram follows naturally: iterate over each possible length and print one * per count. The vertical histogram is the real challenge. You cannot draw column 3 top-to-bottom while reading input, because you do not yet know how tall neighbouring columns will be. The solution is a two-pass approach: after counting, scan wlen to find the maximum bar height, then render rows from top down — printing * at column j in row i if and only if wlen[j] >= i.
One edge case to handle: a word longer than MAXWORD characters must not write past the end of the array. The if (len <= MAXWORD) guard prevents that.
Horizontal Histogram
/* K&R Exercise 1-13 — horizontal word-length histogram
Compile: gcc -ansi -Wall ex1-13.c -o ex1-13 */
#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
#define MAXWORD 20 /* longest word length tracked */
int main(void)
{
int c, state, len, i, j;
int wlen[MAXWORD + 1]; /* wlen[i] = count of words with length i */
for (i = 0; i <= MAXWORD; i++)
wlen[i] = 0;
len = 0;
state = OUT;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\t' || c == '\n') {
if (state == IN) { /* just finished a word */
if (len <= MAXWORD)
++wlen[len];
len = 0;
}
state = OUT;
} else {
++len;
state = IN;
}
}
/* print horizontal histogram: one * per word of that length */
for (i = 1; i <= MAXWORD; i++) {
printf("%2d | ", i);
for (j = 0; j < wlen[i]; j++)
putchar('*');
putchar('\n');
}
return 0;
}
The counting loop uses two variables in tandem. state remembers whether the previous character was part of a word. len grows for every non-whitespace character. When whitespace arrives and state == IN, a word just ended — record its length (capped at MAXWORD), reset len to zero, and flip state to OUT. Non-whitespace increments len and sets state to IN. The display loop then needs just a nested for: outer over lengths 1–20, inner printing wlen[i] asterisks.
Compile and Run
gcc -ansi -Wall ex1-13.c -o ex1-13
echo "the quick brown fox jumped over the lazy dog" | ./ex1-13
The program reads from standard input, so you can also pipe a file:
./ex1-13 < mytext.txt
Sample Output — Horizontal
Input: “the quick brown fox jumped over the lazy dog”
Word lengths: the(3), quick(5), brown(5), fox(3), jumped(6), over(4), the(3), lazy(4), dog(3) — so wlen[3]=4, wlen[4]=2, wlen[5]=2, wlen[6]=1.
1 | 2 | 3 | **** 4 | ** 5 | ** 6 | * 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
Lengths with no words print an empty bar. Lengths 1, 2, and 7 through 20 are empty for this input.
Bonus: Vertical Histogram
The vertical version rotates the chart 90 degrees so bars grow upward and word-length labels run along the bottom axis. The counting loop is identical; only the rendering changes. First, a scan over wlen finds the tallest bar (max). Then, rows are printed from max down to 1: for each row r, walk across all 20 columns and print * if wlen[col] >= r, otherwise a space. A column “reaches” a row only when its count is at least as large as the row number. Finally, print the column labels.
/* K&R Exercise 1-13 — horizontal + vertical word-length histogram
Compile: gcc -ansi -Wall ex1-13v.c -o ex1-13v */
#include <stdio.h>
#define IN 1
#define OUT 0
#define MAXWORD 20
int main(void)
{
int c, state, len, i, j, max;
int wlen[MAXWORD + 1];
for (i = 0; i <= MAXWORD; i++)
wlen[i] = 0;
len = 0;
state = OUT;
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\t' || c == '\n') {
if (state == IN) {
if (len <= MAXWORD)
++wlen[len];
len = 0;
}
state = OUT;
} else {
++len;
state = IN;
}
}
/* --- horizontal histogram --- */
printf("Horizontal:\n");
for (i = 1; i <= MAXWORD; i++) {
printf("%2d | ", i);
for (j = 0; j < wlen[i]; j++)
putchar('*');
putchar('\n');
}
/* --- vertical histogram: two-pass --- */
printf("\nVertical:\n");
/* pass 1: find the tallest bar */
max = 0;
for (i = 1; i <= MAXWORD; i++)
if (wlen[i] > max)
max = wlen[i];
/* pass 2: print rows from top down */
for (i = max; i >= 1; i--) {
for (j = 1; j <= MAXWORD; j++)
putchar(wlen[j] >= i ? '*' : ' ');
putchar('\n');
}
/* x-axis labels: last digit of each column number */
for (i = 1; i <= MAXWORD; i++)
printf("%d", i % 10);
putchar('\n');
return 0;
}
Sample Output — Vertical
Same input. wlen[3]=4, wlen[4]=2, wlen[5]=2, wlen[6]=1. The tallest bar is column 3 with height 4, so 4 rows are printed.
* * *** **** 12345678901234567890
Reading the chart: column 3 rises four rows (four 3-letter words); columns 4 and 5 rise two rows; column 6 rises one row. The x-axis label shows the last digit of each column number, so position 10 shows 0, position 11 shows 1, and so on.
How the Two-Pass Vertical Rendering Works
- Why you cannot do it in one pass: to print the top row you need to know every bar’s height. That is not known until the entire input has been read.
- Finding the ceiling: a simple loop over
wlen[1..MAXWORD]givesmax— the height of the tallest bar and thus the number of rows to print. - The rendering loop: the outer loop runs
ifrommaxdown to 1 (each value is a row height). The inner loop scans columns 1 to 20 and evaluateswlen[j] >= i. A column “reaches” row i only when its count is at least i — otherwise it contributes a space. Printing top-to-bottom in this order produces a correctly oriented chart.
What This Exercise Teaches
- Array as a frequency table. Using
wlen[len]++accumulates counts in O(1) per word. The array index encodes the key; the value encodes the count. This pattern — indexing an array by a measured property — appears constantly in systems programming. - State machine word detection. The IN/OUT flag detects word boundaries without any string library. Responding to transitions (OUT→IN when a word starts, IN→OUT when it ends) rather than individual characters is the generalisation that scales to real parsers and tokenisers.
- Two-pass algorithm design. Some output cannot be generated in a single forward pass. The vertical histogram makes this concrete: gather all data first, then render. Recognising when a second pass is needed — versus when buffering would be the right trade-off — is a fundamental design skill.
- Array bounds guarding. The
if (len <= MAXWORD)check before every array write prevents undefined behaviour from unusually long input words. Learning this habit before pointers and dynamic memory arrive makes it feel natural rather than defensive.
Set Up Your C Environment
To compile and run these solutions you need GCC installed. If you have not yet set up C development on your machine:
- Complete C Development Environment Setup — start here if you are new
- 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 — try it in your browser without installing anything
← Exercise 1-12 |
Chapter 1 Solutions |
Exercise 1-14 →
Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie
1 comment on “K&R C Exercise 1-13: Histogram of Word Lengths”
Hi…
It's really a nice doing by sharing this kind of good programming solutions to learn from it….
Here is 1 that I had written in attempt minimize the size , Hope that it would b helpful…
***************************************************
#include
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* Exercise 1-13. Write a program to print a histogram of the lengths of words in its input. It is
easy to draw the histogram with the bars horizontal; a vertical orientation is more challenging.
*/
// VERTICAL ORIENTATION
int main()
{
int c,nw, nc, state;
int i,j; //Counters
int nchar[100],maxlen=0; /*for holding peak value of no. of char; For max. lenght of a word*/
state = OUT;
nw = nc = 0;
while ((c = getchar()) != EOF)
{
++nc;
if (c ==' ' || c =='n' || c =='t')
{
nc=nc-1;
if(state == IN)
{
nchar[nw]=nc;
if(nc > maxlen)
maxlen = nc;
nc=0;
}
state = OUT;
}
else if (state == OUT)
{
state = IN;
++nw;
}
}
printf("nThe Histogram of the lengths of words:n");
for(i=maxlen ; i>=0 ; i–)
{
for(j=1 ; j<= nw ; j++)
{
if(nchar[j] > i)
putchar('*');
else
putchar(' ');
}
putchar('n');
}
return 0;
}