C / CHAPTER 11

Streams, end-of-file, and error handling

Intermediate

Keep byte values distinct from stream status.

By the end of this chapter

  • Store fgetc results in int
  • Distinguish end-of-file from errors
  • Close owned streams on every exit path

The idea

A FILE pointer represents a C stream whose operations can fail independently of your program’s logic. fgetc returns the next unsigned-char value converted to int, or EOF. Store that result in int until checking EOF; narrowing to char first can lose the distinction between an ordinary byte and the status value. Do not use feof as a prediction that the next read will succeed.

After a read loop stops, inspect ferror when you need to distinguish a failure from ordinary end-of-file. Check creation and write results as well as reads. A function that owns a stream should close it on every relevant exit path, while a function borrowing a caller’s stream should follow the caller’s ownership contract. The demonstration uses a temporary stream and reports output only on successful I/O.

C example
#include <stdio.h>
int main(void) {
  FILE *file = tmpfile();
  if (!file) return 1;
  if (fputs("AB", file) == EOF) { fclose(file); return 1; }
  rewind(file);
  int ch, count = 0;
  while ((ch = fgetc(file)) != EOF) ++count;
  if (ferror(file)) { fclose(file); return 1; }
  printf("%d\n", count);
  return fclose(file) == 0 ? 0 : 1;
}
Result
2 on the successful temporary-stream path.

The loop counts the two bytes and stops on the read that reports EOF.

Try it yourself

Count newline bytes in a borrowed stream and return a status that distinguishes success from a read error.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Which variable type should hold fgetc’s result before EOF is checked?