C File Error Handling, EOF Detection, ferror() & errno Masterclass

โšก C (C17 / C23 Standard) ๐ŸŸข Lesson 38 ๐Ÿ“‚ Phase 14: File Handling & I/O Streams ๐Ÿ“… 2026 Comprehensive Master Edition
๐Ÿ“Œ Covered in this in-depth guide: feof() EOF Detection ยท ferror() Error Checking ยท System Errors ยท perror() & strerror() ยท File Copy & Log File Utility Programs

Welcome to Phase 14 (Chapter 38): C File Error Handling, EOF Detection, ferror() & errno Masterclass! Production C software must gracefully handle runtime I/O failures (missing files, full disks, invalid permissions, hardware disconnects). In this guide, you will master system error reporting with <errno.h>, perror(), feof(), and ferror().

1feof() vs ferror() Mechanics

When an I/O operation returns an end-of-file condition or failure indicator, C provides two distinct diagnostic status checkers:

feof() vs ferror() Matrix:

โ€ข feof(FILE *fp): Returns non-zero (true) if stream reached End-Of-File normally.

โ€ข ferror(FILE *fp): Returns non-zero (true) if stream encountered a Hardware or System I/O Error.

โ€ข clearerr(FILE *fp): Clears both EOF and error flags for the given stream.

โš ๏ธ The feof() Loop Bug Trap:

DO NOT write while (!feof(fp))! feof() becomes true ONLY AFTER a read operation attempts to read past the end of the file and fails.

2System Error Reporting with <errno.h>

When standard library functions fail, they set a thread-global integer variable errno defined in <errno.h> to an OS-specific error code:

errno ConstantInteger CodeMeaning
ENOENT2No such file or directory.
EACCES13Permission denied (Read/Write forbidden).
EEXIST17File already exists.
ENOSPC28No space left on device (Disk Full).

C provides 2 helper functions to convert errno integers into human-readable messages:
โ€ข perror("Custom Prefix"): Prints prefix + descriptive system message to stderr.
โ€ข strerror(errno): Returns const char* string message from <string.h>.

3Comprehensive Production Code Example
C โ€” Robust Production File Copying Utilityโ–ถ Run Code in C Compiler
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#define BUFFER_SIZE 4096

int copyFile(const char *srcPath, const char *destPath) {
    FILE *src = fopen(srcPath, "rb");
    if (!src) {
        fprintf(stderr, "Error opening source '%s': %s (Errno %d)\n", srcPath, strerror(errno), errno);
        return -1;
    }
    FILE *dest = fopen(destPath, "wb");
    if (!dest) {
        fprintf(stderr, "Error opening destination '%s': %s\n", destPath, strerror(errno));
        fclose(src);
        return -1;
    }
    unsigned char buffer[BUFFER_SIZE];
    size_t bytesRead, bytesWritten;
    while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {
        bytesWritten = fwrite(buffer, 1, bytesRead, dest);
        if (bytesWritten < bytesRead) {
            perror("Disk Write Failure");
            fclose(src); fclose(dest);
            return -1;
        }
    }
    if (ferror(src)) {
        fprintf(stderr, "Hardware Read Failure on '%s'\n", srcPath);
        fclose(src); fclose(dest);
        return -1;
    }
    if (feof(src)) {
        printf("File copy successful! All bytes transferred safely.\n");
    }
    fclose(src); fclose(dest);
    return 0;
}

int main(void) {
    copyFile("non_existent_input.bin", "output_copy.bin");
    return 0;
}
4Technical FAQs

Q1: Why does perror() print to stderr instead of stdout?

Printing error logs to stderr ensures diagnostic messages are visible in console output even when standard stdout is redirected to a file or pipe.

Q2: Must we clear errno before calling a library function?

Yes! Library functions set errno on failure but DO NOT reset errno to 0 on success.

Q3: What function clears stream error flags?

Calling clearerr(fp) resets both feof and ferror indicators for the specified stream handle.

Q4: How do you append timestamped logs safely to disk?

Open the file with fopen("app.log", "a"). Append operations atomically jump to the end of file before every write.

Q5: What is the return value of strerror_s or strerror_r?

They are thread-safe versions of strerror() introduced to prevent race conditions in multithreaded applications.