Skip to content
  • Error Handling: errno & Return Codes · C

Error Handling: errno & Return Codes

LearnViaNotes @learnvianotes· 3 min read

C has no exceptions. Functions report failure through their return value (NULL, -1, EOF), and the library adds detail in the global errno. Checking every result is the programmer’s job.

Library conventions

Function kindFailure signalDetails
Returns a pointer (fopen, malloc)NULLerrno set (by POSIX)
Returns a count (fread, scanf)Fewer items than requested / EOFfeof / ferror
POSIX system calls (open, read)-1errno
Conversions (strtol)Result + end pointererrno == ERANGE on overflow
Math (sqrt(-1), log(0))NaN / ±HUGE_VALerrno == EDOM / ERANGE (if math_errhandling says so)

errno, strerror, perror

C
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

int main(void) {
    FILE *f = fopen("does-not-exist.txt", "r");
    if (f == NULL) {
        int saved = errno;                 /* save it: the next call may overwrite it */
        printf("fopen failed: errno=%d (%s)\n", saved, strerror(saved));
        printf("is ENOENT? %s\n", saved == ENOENT ? "yes" : "no");
        perror("fopen");                   /* prints "fopen: No such file..." to stderr */
    }

    errno = 0;                             /* strtol only sets errno on error: reset first */
    long v = strtol("99999999999999999999", NULL, 10);
    if (errno == ERANGE) printf("overflow, clamped to %ld\n", v);
    return 0;
}
Output
fopen failed: errno=2 (No such file or directory)
is ENOENT? yes
overflow, clamped to 9223372036854775807

Designing your own error reporting

C
#include <stdio.h>

typedef enum { ERR_OK = 0, ERR_EMPTY, ERR_RANGE, ERR_PARSE } Err;

const char *err_str(Err e) {
    static const char *msg[] = {"ok", "empty input", "out of range", "not a number"};
    return msg[e];
}

/* return the status; deliver the result through an out-parameter */
Err parse_percent(const char *s, int *out) {
    if (!s || !*s) return ERR_EMPTY;
    int v = 0;
    for (; *s; s++) {
        if (*s < '0' || *s > '9') return ERR_PARSE;
        v = v * 10 + (*s - '0');
        if (v > 100) return ERR_RANGE;
    }
    *out = v;
    return ERR_OK;
}

int main(void) {
    const char *inputs[] = {"42", "", "250", "4x"};
    for (int i = 0; i < 4; i++) {
        int p;
        Err e = parse_percent(inputs[i], &p);
        if (e == ERR_OK) printf("'%s' -> %d%%\n", inputs[i], p);
        else printf("'%s' -> error: %s\n", inputs[i], err_str(e));
    }
    return 0;
}
Output
'42' -> 42%
'' -> error: empty input
'250' -> error: out of range
'4x' -> error: not a number

Cleanup with goto

C
#include <stdio.h>
#include <stdlib.h>

int process(const char *path) {
    int rc = -1;
    char *buf = NULL;
    FILE *f = fopen(path, "w+");
    if (!f) goto out;

    buf = malloc(64);
    if (!buf) goto close_file;

    if (fputs("data\n", f) == EOF) goto free_buf;
    rewind(f);
    if (!fgets(buf, 64, f)) goto free_buf;
    printf("read back: %s", buf);
    rc = 0;                                 /* success */

free_buf:                                   /* release in reverse order of acquisition */
    free(buf);
close_file:
    fclose(f);
out:
    return rc;
}

int main(void) {
    printf("rc = %d\n", process("work.txt"));
    printf("rc = %d\n", process("no-such-dir/work.txt"));
    return 0;
}
Output
read back: data
rc = 0
rc = -1

Terminating

CallEffect
return n; from mainSame as exit(n)
exit(status)Runs atexit handlers, flushes and closes streams, then ends the process
_Exit(status) / _exitEnds immediately — no handlers, no flushing
abort()Raises SIGABRT — abnormal termination, core dump; used by assert
quick_exit (C11)Runs at_quick_exit handlers only

How do you handle errors in C without exceptions?

Is errno thread-safe?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report