thecodex.expert · The Codex Family of Knowledge
C

Error Handling

There is no try/catch in C — if you don’t check a function’s return value for failure yourself, that error simply vanishes and the program keeps running on bad data.

C17 / C23 No exceptions — check every return Last verified:
Canonical Definition

Without exceptions, throw, or try/catch, C functions signal failure through their return value — commonly NULL for a function returning a pointer, -1 for a function returning an otherwise-nonnegative int, or an explicit status/error code. Many standard library functions additionally set the global errno variable to a specific error code on failure, which strerror() can translate into a human-readable message. Critically, nothing forces a caller to check any of this — an ignored error return simply lets execution continue with invalid data or a null pointer, silently, until something else breaks later.

Checking return values: entirely manual, entirely optional

Nothing forces this check to happen — skip it, and a failed fopen() silently returns NULL, which then crashes the program the instant something tries to read from it, often several lines away from the actual mistake.

Ccheck_return.c
FILE *f = fopen("data.txt", "r");
if (f == NULL) {                    // MUST check manually — nothing does this for you
    perror("fopen failed");            // prints a message using errno's current value
    return 1;
}
// safe to use f from here

errno and strerror: more detail on WHY it failed

errno alone is just a numeric code; strerror() translates it into a readable message — always check errno immediately after the failing call, since a subsequent successful function call can overwrite it.

Cerrno_example.c
#include <errno.h>
#include <string.h>

FILE *f = fopen("missing.txt", "r");
if (f == NULL) {
    printf("Error: %s\n", strerror(errno));   // e.g. "No such file or directory"
}

A common pattern: an explicit status/result type

Returning a dedicated enum status alongside output through a pointer parameter is a common idiom for functions that genuinely can't use a sentinel value (like -1 or NULL) to mean "failure," because every possible return value is a legitimate success case.

Cstatus_pattern.c
typedef enum { OK, ERR_DIVIDE_BY_ZERO } Status;

Status safeDivide(int a, int b, int *result) {
    if (b == 0) {
        return ERR_DIVIDE_BY_ZERO;
    }
    *result = a / b;
    return OK;
}

int result;
if (safeDivide(10, 0, &result) != OK) {
    printf("division failed\n");
}

Sources

1
ISO/IEC 9899 (C Standard), "Errors <errno.h>," cppreference.com/w/c/error.