2026-05-23
The asker presents a common defensive idiom and asks a subtle question about it:
while (fgets(s, sizeof(s), f) != NULL) {
if (feof(f)) {
break;
}
}
Consider the case where fgets reads some characters, then encounters end-of-file before reading a newline. It returns a non-NULL pointer (because something was read), but the stream has hit EOF. The question: does the standard guarantee that feof(f) returns true on that very iteration, or only on the next call to fgets (which would then return NULL)?
Why this is interesting: The C standard is famously cagey about when exactly the EOF indicator gets set. The asker correctly notes the conventional wisdom that feof should be checked after a read fails, not as a loop condition. But that wisdom is about correctness of loop termination, not about whether the indicator is set eagerly or lazily inside a single read call.
What the standard actually says. In C17 §7.21.7.2, fgets is specified to "read at most one less than the number of characters specified by n" until either n-1 characters are read, a newline is encountered (and stored), or end-of-file is encountered. The function returns NULL only "if end-of-file is encountered and no characters have been read into the array." There is no explicit prose stating that the EOF indicator is set during a partial-but-successful read.
However, §7.21.3 ¶12 says the end-of-file indicator is set "when end-of-file is encountered" during input operations, and fgets is defined in terms of fgetc-like character reads (via the abstract machine model in §7.21.7.2 ¶2). Each underlying character fetch that hits EOF sets the indicator. So if fgets stopped because it hit EOF mid-line, the indicator should already be set when it returns.
The honest answer. In practice, yes — every mainstream implementation (glibc, musl, MSVC) sets the indicator as soon as the underlying read sees EOF, before fgets returns. The standard arguably mandates this via the "as-if read by fgetc" specification, but it doesn't say so in one tidy sentence. That ambiguity is exactly why the canonical advice is "check feof only after a read returns failure" — that pattern works regardless of how eagerly the indicator is set.
Gotchas:
ferror, otherwise you treat I/O errors as clean EOF.\n means the EOF-detecting read happens on the next fgets call, which returns NULL with feof true. The break-mid-loop pattern only fires when the final line lacks a terminator.feof check entirely, since the next iteration will return NULL anyway.fgets, versus merely allowing it — a question the standard answers only by inference through its abstract character-read model.