K&R Exercise 1-13. Printing histogram of word lengths (horizontal variant) I'm going through the K&amp;R book (2nd edition, ANSI C ver.) and want to get the most from it. How does the following solution look to you? Note that, for the sake of exercise, I don't want to use techniques not introduced yet in the book. Also, I'm trying to reuse whatever code/philosophy already presented in the book.

/* 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.
 * */
/* Solution 1: Horizontal Bars
 * */
#include &lt;stdio.h&gt;

#define MAXLEN  10  /* Any word longer than this will get counted in the &gt;MAXLEN histogram.
                     * For output formatting, the program assumes this won't be greater than 999.
                     * */
#define IN      1   /* inside a word */
#define OUT     0   /* outside a word */

int main()
{
    int c, state;
    int i, j;
    int histo[MAXLEN+1];
    int counter;

    for (i = 0; i &lt; MAXLEN+1; ++i)
        histo[i] = 0;

    /* Perform the counting */
    state = OUT;
    counter = 0;
    while ((c = getchar()) != EOF)
        if (c == ' ' || c == '\n' || c == '\t') {
            if (state == IN &amp;&amp; counter &gt; 0) {
                if (counter-1 &lt; MAXLEN)
                    ++histo[counter-1];
                else
                    ++histo[MAXLEN];
            }
            state = OUT;
            counter = 0;
        }
        else {
            ++counter;
            if (state == OUT)
                state = IN;
        }

    /* Print horizontal histogram. I'd use a function for formatting
     * with regards to max. number of digits, but functions haven't
     * been introduced yet.
     * */
    for (i = 0; i &lt; MAXLEN; ++i) {
        if (MAXLEN &lt; 10)
            printf(" %1d | ", i+1);
        else if (MAXLEN &lt; 100)
            printf(" %2d |", i+1);
        else
            printf(" %3d |", i+1);
        for (j = 0; j &lt; histo[i]; ++j)
            putchar('*');
        putchar('\n');
    }
    if (MAXLEN &lt; 10)
        printf("&gt;%1d |", i);
    else if (MAXLEN &lt; 100)
        printf("&gt;%2d |", i);
    else
        printf("&gt;%3d |", i);
    for (j = 0; j &lt; histo[i]; ++j)
        putchar('*');
    putchar('\n');
}


Output when run on the program code:

$ cat ch1-ex-1-13-01.c | ./ch1-ex-1-13-01
  1 |*********************************************************
  2 |****************************************************************
  3 |***************************************
  4 |************************************
  5 |**************
  6 |**********
  7 |************************
  8 |***********
  9 |*******
 10 |*********
&gt;10 |**************
K&amp;R C requires all variables to be declared at the start of their enclosing block.  This is a bad habit to learn: it's much safer to declare variables where they can be initialised:
/* these can already be initialised where they are declared */
int state = OUT;
int histo[MAXLEN+1] = { 0 };  /* C90 */
int counter = 0;


/* reduce scope, and never have uninitialized value */
for (int j = 0; j &lt; histo[i]; ++j)  /* C99 */


We could remove the need to subtract 1 from the length when updating histo if we reserve the first element for long words instead of the last:
        if (state == IN &amp;&amp; counter &gt; 0) {
            if (counter &lt;= MAXLEN)
                ++histo[counter];
            else
                ++histo[0];
        }

We obviously need to make a corresponding change to the printing:
for (int i = 1;  i &lt;= MAXLEN;  ++i) {
    if (MAXLEN &lt; 10)
        printf(&quot; %1d | &quot;, i);
    else if (MAXLEN &lt; 100)
        printf(&quot; %2d |&quot;, i);
    else
        printf(&quot; %3d |&quot;, i);
    for (int j = 0;  j &lt; histo[i];  ++j)
        putchar('*');
    putchar('\n');
}


We can avoid the if/else chain on MAXLEN in this loop, by choosing the formatting string in advance:
const char *format = &quot;&quot;;
if (MAXLEN &lt; 10) {
        format = &quot; %1d | &quot;;
} else if (MAXLEN &lt; 100) {
        format = &quot; %2d | &quot;;
}  else {
        format = &quot; %3d | &quot;;
}

for (int i = 1;  i &lt;= MAXLEN;  ++i) {
    printf(format, i);
    for (int j = 0;  j &lt; histo[i];  ++j)
        putchar('*');
    putchar('\n');
}

That might not look like an improvement, but with a small change, we can use the same format string to insert &gt; for the over-long words, too:
const char *format = &quot;&quot;;
if (MAXLEN &lt; 10) {
        format = &quot;%c%1d | &quot;;
} else if (MAXLEN &lt; 100) {
        format = &quot;%c%2d | &quot;;
}  else {
        format = &quot;%c%3d | &quot;;
}

for (int i = 1;  i &lt;= MAXLEN;  ++i) {
    printf(format, ' ', i);
    for (int j = 0;  j &lt; histo[i];  ++j)
        putchar('*');
    putchar('\n');
}

/* over-long words */
printf(format, '&gt;', MAXLEN);
for (int j = 0; j &lt; histo[0]; ++j)
    putchar('*');
putchar('\n');

Going beyond the expected level of knowledge, we could even compute the necessary length, and pass it using %*d:
int width = 1;
for (int i = MAXLEN;  i &gt;= 10;  i /= 10) {
    ++width;
}

for (int i = 1;  i &lt;= MAXLEN;  ++i) {
    printf(&quot; %*d | &quot;, width, i);
    for (int j = 0;  j &lt; histo[i];  ++j)
        putchar('*');
    putchar('\n');
}

/* over-long words */
printf(&quot;&gt;%*d | &quot;, width, MAXLEN);
for (int j = 0; j &lt; histo[0]; ++j)
    putchar('*');
putchar('\n');

That allows us to have non-constant MAXLEN, which will be to our advantage in future when we make this a function.

Whilst the state machine is a good idea, it turns out we don't need to explicitly store state, because counter is always zero outside a word and non-zero once we're in one:
while ((c = getchar()) != EOF) {
    if (c == ' ' || c == '\n' || c == '\t') {
        if (counter &gt; 0) {
            ++histo[counter &lt;= MAXLEN ? counter : 0];
            counter = 0;
        }
    } else {
        ++counter;
    }
}


Did you spot the bug in this loop?  Probably not, because the bug is missing code immediately after the loop.  If the input stream ends during a word, we fail to count it.  We need to repeat the if (counter &gt; 0) test just after the loop, to catch that case.

We have an unusual definition of &quot;word&quot; - in our source code, strings such as ++histo[counter-1]; count as single words.  Even in English text, we'd expect text, to register as a four-character word.  To fix this, we could consider only alphanumerics as word characters, and that could be a good introduction to the functions in &lt;ctype.h&gt;.
while ((c = getchar()) != EOF) {
    if (isalnum(c)) {
        ++counter;
    } else {
        if (counter &gt; 0) {
            ++histo[counter &lt;= MAXLEN ? counter : 0];
            counter = 0;
        }
    }
}

if (counter &gt; 0) {
    ++histo[counter &lt;= MAXLEN ? counter : 0];
}


Enhancement - we could avoid bars going off the right edge of the display, by calculating a scale factor first.  Here's my suggestion:
#define MAXWIDTH 72             /* screen width available for bars */


    double scale = 1.0;
    for (int i = 0;  i &lt;= MAXLEN;  ++i) {
        if (histo[i] * scale &gt; MAXWIDTH) {
            scale = 1.0 * MAXWIDTH / histo[i];
        }
    }


        for (int j = 0;  j &lt; histo[i] * scale;  ++j) { putchar('*'); }


Finally: it's not part of your C code, but there's no need for cat in the test.  We can simply redirect input like this:
./ch1-ex-1-13-01 &lt;ch1-ex-1-13-01.c


Modified code
Applying all my suggestions, I got:
/* 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. */

/* Solution 1: Horizontal Bars
 * */
#include &lt;ctype.h&gt;
#include &lt;stdio.h&gt;

#define MAXLEN  10  /* Any word longer than this will get counted in the
                       &gt;MAXLEN histogram. */

#define MAXWIDTH 72             /* screen width available for bars */

int main()
{
    int histo[MAXLEN+1] = { 0 }; /* element 0 counts over-length words */

    /* Perform the counting */
    int counter = 0;
    int c;
    while ((c = getchar()) != EOF) {
        if (isalnum(c)) {
            ++counter;
        } else {
            if (counter &gt; 0) {
                ++histo[counter &lt;= MAXLEN ? counter : 0];
                counter = 0;
            }
        }
    }

    if (counter &gt; 0) {
        ++histo[counter &lt;= MAXLEN ? counter : 0];
    }

    /* Calculate sensible scale */
    double scale = 1.0;
    for (int i = 0;  i &lt;= MAXLEN;  ++i) {
        if (histo[i] * scale &gt; MAXWIDTH) {
            scale = 1.0 * MAXWIDTH / histo[i];
        }
    }

    /* how wide are the labels? */
    int width = 1;
    for (int i = MAXLEN;  i &gt;= 10;  i /= 10) {
        ++width;
    }

    /* Write the output */
    for (int i = 1;  i &lt;= MAXLEN;  ++i) {
        printf(&quot; %*d | &quot;, width, i);
        for (int j = 0;  j &lt; histo[i] * scale;  ++j) { putchar('*'); }
        putchar('\n');
    }

    /* over-long words */
    printf(&quot;&gt;%*d | &quot;, width, MAXLEN);
    for (int j = 0; j &lt; histo[0] * scale; ++j) { putchar('*'); }
    putchar('\n');
}