C's getchar() Return Type Trap: The char That Can't Tell EOF from 0xFF

2026-07-02

This function is supposed to count occurrences of a target byte on stdin, reading until end-of-file. It compiles clean under -Wall -Wextra on most compilers, passes its ASCII test suite, and ships to production. Then someone pipes a UTF-8 file through it, or a JPEG, and things get weird.

#include <stdio.h>

int count_byte(int target) {
    int count = 0;
    char c;
    while ((c = getchar()) != EOF) {
        if (c == target) count++;
    }
    return count;
}

Depending on the platform, this loop either terminates too early on binary input, or never terminates at all on any input. Both failure modes come from the same three-character mistake.

The Bug

getchar() returns an int, not a char, and it does so for a very specific reason: it needs to return every possible byte value (0 through 255) plus the sentinel EOF, which is a negative integer (typically -1). That's 257 distinct values — one more than fits in a byte.

By storing the result in char c, we crush that 257-value space into 256. What happens next depends on whether char is signed or unsigned on the target platform — and the C standard leaves that implementation-defined.

The same platform can pass your CI on ASCII fixtures and fail catastrophically on real user data. Worse, cross-compiling to a different architecture can flip the failure mode from "wrong count" to "hang forever" without a single line of code changing.

The Fix

Store the return value in an int. Always. This is not a style preference — it is the only correct way to consume getchar, fgetc, or getc.

#include <stdio.h>

int count_byte(int target) {
    int count = 0;
    int c;
    while ((c = getchar()) != EOF) {
        if (c == target) count++;
    }
    return count;
}

Now c can distinguish all 257 possible return values. The byte 0xFF arrives as 255, EOF arrives as -1, and the comparison works on every platform.

A telltale sign this bug is lurking: any loop of the shape while ((char_variable = getchar()) != EOF). If your reviewer catches nothing else in a C code review, catch this. It has been shipping in student code and production code for fifty years, and it will keep shipping until char and int stop being confusable at assignment.

Key Takeaway: getchar() returns int because it needs one more value than char can hold — storing its result in a char either truncates EOF into a valid byte (infinite loop) or aliases byte 0xFF onto EOF (early termination), and which disaster you get depends on your platform's signedness of char.

All newsletters