getchar() Return Type Trap: The char That Can't Tell EOF from 0xFF2026-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.
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.
char is signed (x86 Linux, macOS): reading the byte 0xFF stores -1 in c. The comparison c != EOF promotes c back to int as -1, which equals EOF. The loop terminates the first time your input contains byte 0xFF — extremely common in UTF-8 (it's part of many multi-byte sequences), JPEG, ZIP, and virtually any binary format.char is unsigned (ARM Linux, PowerPC, many embedded targets): the real EOF value -1 gets truncated to 0xFF (255) when stored in c. Comparing c != EOF promotes it back to int as 255, which is never equal to -1. The loop runs forever, and every subsequent getchar() after EOF keeps returning -1, which keeps getting stored as 255, which keeps not matching. Infinite loop, 100% CPU.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.
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.
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.
