SyntaxStudy
Sign Up
C Error Handling and File Utilities
C Beginner 1 min read

Error Handling and File Utilities

Robust file handling requires checking return values at every step. `fopen` can fail if the file doesn't exist, permissions are wrong, or the system is out of file descriptors. `fread` and `fwrite` can transfer fewer elements than requested. After a partial read, `feof()` checks whether end-of-file was reached and `ferror()` checks whether an I/O error occurred. Combining these checks gives precise, actionable error messages. The `perror()` function prints a human-readable error message for the last system error (stored in `errno`). It automatically appends the system's description of the error code. `strerror(errno)` returns the error string without printing it, which is useful for building custom error messages with `fprintf`. After reporting an error, you should typically close any open files, free allocated memory, and return a failure code rather than proceeding with corrupted state. Reading an entire file into memory is a common operation. The pattern is: open the file, seek to the end to get the size, allocate a buffer of that size plus one, seek back to the start, read the entire file, null-terminate the buffer, and close the file. This technique is useful for parsing configuration files, source code, or JSON payloads. Always add one extra byte for the null terminator when reading text files this way.
Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* Read entire file into a malloc\'d buffer (caller must free) */
char *read_file(const char *path, long *out_size)
{
    char *buf  = NULL;
    FILE *f    = NULL;
    long  size = 0;

    f = fopen(path, "rb");
    if (!f) {
        fprintf(stderr, "fopen '%s': %s\n", path, strerror(errno));
        goto done;
    }

    if (fseek(f, 0, SEEK_END) != 0) { perror("fseek"); goto done; }
    size = ftell(f);
    if (size < 0)                   { perror("ftell"); goto done; }
    rewind(f);

    buf = malloc((size_t)size + 1);
    if (!buf)                       { perror("malloc"); goto done; }

    size_t got = fread(buf, 1, (size_t)size, f);
    if ((long)got != size && ferror(f)) {
        perror("fread");
        free(buf);
        buf = NULL;
        goto done;
    }
    buf[got] = '\0';   /* null-terminate for text use */
    if (out_size) *out_size = (long)got;

done:
    if (f) fclose(f);
    return buf;
}

int main(void)
{
    /* Write a test file first */
    FILE *f = fopen("test.txt", "w");
    if (f) {
        fprintf(f, "Line one\nLine two\nLine three\n");
        fclose(f);
    }

    long sz;
    char *content = read_file("test.txt", &sz);
    if (content) {
        printf("Read %ld bytes:\n%s\n", sz, content);
        free(content);
    }

    /* Demonstrate error path */
    char *missing = read_file("no_such_file.txt", NULL);
    if (!missing) printf("Correctly handled missing file.\n");

    return EXIT_SUCCESS;
}