Cerrno & perror()

errno & perror()

Many standard library functions signal failure through their return value alone (like NULL or -1), but that doesn't tell you why they failed. To fill that gap, C provides a global variable, errno, declared in <errno.h>, that many library functions set to a specific error code when they fail.

What errno Is

errno is an integer (in modern C, typically a thread-local variable behind the scenes) that gets set to a nonzero value describing the last error when a function fails. Common values include ENOENT (no such file or directory), EACCES (permission denied), and ENOMEM (out of memory).

Check errno Only Immediately After a Failed Call
Warning
`errno` is **not cleared automatically** on success. If a function succeeds, it may leave `errno` set to whatever a previous failed call set it to. This means checking `errno` is only meaningful **immediately** after you've confirmed the specific call actually failed (via its return value) — never check `errno` first and infer failure from it being nonzero.

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

int main(void) {
    FILE *file = fopen("does_not_exist.txt", "r");

    /* Correct order: check the return value FIRST, then consult errno. */
    if (file == NULL) {
        printf("fopen failed, errno = %d\n", errno);
    }

    return 0;
}
perror(): Human-Readable Error Messages

perror(message) prints message, followed by a colon and a human-readable description of the current errno value, to stderr. It's the quickest way to report "something failed, and here's why" without manually translating error codes yourself.

C
#include <stdio.h>

int main(void) {
    FILE *file = fopen("does_not_exist.txt", "r");

    if (file == NULL) {
        perror("fopen"); /* e.g. prints: fopen: No such file or directory */
        return 1;
    }

    fclose(file);
    return 0;
}
strerror(): Getting the Message as a String

Sometimes you want the human-readable message as a string, rather than having it printed directly (for example, to embed it in a log line with more context). strerror(errno) returns a pointer to that message.

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

int main(void) {
    FILE *file = fopen("does_not_exist.txt", "r");

    if (file == NULL) {
        fprintf(stderr, "Failed to open file: %s\n", strerror(errno));
        return 1;
    }

    fclose(file);
    return 0;
}
Worked Example: Reporting Why a File Failed to Open

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

int main(void) {
    const char *filename = "/root/protected-or-missing.txt";

    errno = 0; /* optional: reset explicitly before the call, for clarity */
    FILE *file = fopen(filename, "r");

    if (file == NULL) {
        fprintf(stderr, "Could not open \"%s\": %s (errno %d)\n",
                filename, strerror(errno), errno);
        return 1;
    }

    printf("File opened successfully.\n");
    fclose(file);
    return 0;
}
Note
Resetting `errno = 0` before a call is a defensive habit some codebases use, but it's optional — the essential rule is simply: always check the function's own return value first, and only trust `errno` immediately afterward, before any other library call has a chance to overwrite it.
perror vs strerror

Function

Output

When to use

perror(msg)

Writes directly to stderr

Quick, simple error reporting to the console

strerror(errno)

Returns a string you control

Building a custom log message or error format

Tip
Many POSIX functions (like `open`, `read`, `write`, and the low-level socket API) rely on `errno` far more heavily than the higher-level `<stdio.h>` functions do — get comfortable with this pattern early, since it becomes central once you move into systems and network programming.