K&R C Exercise 1-16: Print True Length of Long Input Lines

Exercise 1-16. Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text.

The K&R longest-line program (Section 1.9) stores each input line in a fixed buffer of MAXLINE characters. That design silently truncates any line longer than the buffer — worse, the reported length is wrong because the original getline stops counting at the buffer boundary. The fix requires separating two concerns that the original code conflated: how many characters we store versus how many the line actually contains. The solution uses two counters inside the revised getline2: i tracks where the next character lands in the buffer (capped at MAXLINE-1), and len keeps incrementing until the newline or EOF regardless of whether the buffer is full. The caller in main receives the true length and prints it, while the buffer holds the text that fit — satisfying both requirements of the exercise.

Solution

/* Compile: gcc -ansi -Wall ex1-16.c -o ex1-16 */

#include <stdio.h>

#define MAXLINE 1000

int getline2(char line[], int maxline);
void copy(char to[], char from[]);

int main(void)
{
    int len, max;
    char line[MAXLINE], longest[MAXLINE];

    max = 0;
    while ((len = getline2(line, MAXLINE)) > 0) {
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    }
    if (max > 0)
        printf("%d: %s", max, longest);
    return 0;
}

/*
 * getline2: read a line into line[], return the TRUE length of the line.
 * Up to maxline-1 characters are stored; the rest are counted but discarded.
 */
int getline2(char line[], int maxline)
{
    int c, i, len;

    len = 0;          /* true character count for this line */
    i   = 0;          /* index into the storage buffer      */

    while ((c = getchar()) != EOF && c != '\n') {
        if (i < maxline - 1)   /* buffer still has room */
            line[i++] = c;
        ++len;                 /* always count, even past the buffer */
    }

    if (c == '\n') {
        if (i < maxline - 1)
            line[i++] = '\n';
        ++len;
    }

    line[i] = '\0';
    return len;        /* true length, not capped at maxline */
}

/* copy: copy from[] into to[]; assumes to[] is large enough */
void copy(char to[], char from[])
{
    int i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

The Two-Counter Trick Explained

The key difference from the original program is inside the while loop of getline2:

  • i — the buffer index. The guard i < maxline - 1 prevents overflow, so i never grows past the buffer boundary.
  • len — the true line length. It is incremented unconditionally on every character, even after the buffer is full.

When the loop ends, i tells us how many characters were stored and len tells us how many characters the input line actually contained. The function returns len, so main always compares and prints the correct length. The stored text — “as much as possible” — is whatever fit inside MAXLINE-1 characters.

The function is named getline2 rather than getline to avoid a name collision with the POSIX getline() function declared in <stdio.h> on modern systems.

Compile and Run

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

Type lines of input followed by Enter. Press Ctrl+D (Linux/macOS) or Ctrl+Z then Enter (Windows) to signal end-of-file. The program prints the true length and content of the longest line found.

To test with a line longer than MAXLINE, use a shell trick:

python3 -c "print('A' * 1500)" | ./ex1-16

Sample Output

Input (three lines typed):
  hello world
  the quick brown fox jumps over the lazy dog
  hi

Output:
  44: the quick brown fox jumps over the lazy dog

With the 1500-character test line (buffer is 1000):

  1501: AAAA...AAA
        (only the first 999 A's are printed, but the length 1501 is correct)

Compare this to the original K&R program, which would report a length of 1000 — the wrong answer — because it stopped counting at the buffer boundary.

What This Exercise Teaches

  • Separating buffer position from logical length. Two counters doing different jobs inside the same loop is a fundamental C pattern that reappears in parsers, I/O routines, and network code.
  • Fixed-size buffer discipline. The guard i < maxline - 1 (leaving room for '\0') is the canonical way to prevent buffer overflow without allocating dynamic memory.
  • Reading past a full buffer. Continuing to consume characters after the buffer is full is necessary both for correctness (true length) and to keep the input stream in a known state for the next call.
  • Name collision awareness. POSIX added getline() to <stdio.h>; renaming to getline2 avoids a silent conflict that would break the build on Linux with -D_POSIX_C_SOURCE or -D_GNU_SOURCE.

Set Up Your C Environment

To compile and run this solution, you need GCC installed. If you haven’t set up C on your machine yet:

← Exercise 1-15  | 
Chapter 1 Solutions  | 
Exercise 1-17 →

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>