Skip to Content
Course content

120: Error Codes with errno.h

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've likely already encountered a situation where a C function returns NULL or -1, and your only reaction is to print "An error occurred" to the console. While that tells you the program failed, it doesn't tell you why. Was the file missing? Did you lack the permissions to read it? Did the disk run out of space?

Take a look at this snippet. It's a typical way a beginner tries to load a configuration file:

#include <stdio.h>

int main() {
    FILE *config = fopen("config.txt", "r");
    if (config == NULL) {
        printf("Error: Could not open config.txt\n");
        return 1;
    }
    // ... read file ...
    fclose(config);
    return 0;
}

The mystery of the NULL pointer

If you run this and config.txt isn't there, you get your error message. But what if the file is there, but the current user doesn't have read permissions? Or what if the path is actually a directory instead of a file? The output is exactly the same: "Error: Could not open config.txt".

As a developer, this is frustrating. When your code crashes in production on a server you don't have direct access to, "Could not open file" isn't enough information to fix the bug. You're essentially flying blind.

Letting errno tell the story

Most system calls and standard library functions in C don't just return a failure code; they set a global (strictly speaking, thread-local) variable called errno. This integer acts as a diagnostic code that tells you exactly what went wrong.

To use it, you need <errno.h>. You can then use perror() to print a human-readable version of the error, or strerror() from <string.h> if you want to format the message yourself. Here is how I would rewrite that config loader:

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

int main() {
    FILE *config = fopen("config.txt", "r");
    
    if (config == NULL) {
        // Option 1: The quick way. 
        // perror prints your string, followed by the current errno message.
        perror("Error opening config.txt");

        // Option 2: The custom way.
        // strerror converts the integer errno into a string.
        printf("Detailed log: %s (Error code: %d)\n", strerror(errno), errno);
        
        return 1;
    }
    
    fclose(config);
    return 0;
}

Now, if the file is missing, you'll see: Error opening config.txt: No such file or directory. If permissions are the issue, you'll see: Error opening config.txt: Permission denied. That distinction is the difference between spending five minutes fixing a chmod issue and five hours searching for a "missing" file that was actually there all along.

The golden rule of checking errno

Here is a nuance that trips up even experienced devs: Never check errno unless the function you just called actually indicated a failure.

Standard C functions are not required to reset errno to 0 when they succeed. If a previous, unrelated function call set errno to EACCES (Permission Denied) and then your current function succeeds, errno will still hold that old value. If you check it without checking the return value first, you'll think your successful call actually failed.

The workflow should always be:

  • Call the function.
  • Check the return value (e.g., if (result == -1)).
  • Only then look at errno to find out why.



📋 Practical Task

The "Robust File Access" Tool

Create a program that attempts to open three different files: "existing.txt" (which you should create before running), "nonexistent.txt", and "/etc/shadow" (or any file on your system that you know exists but you don't have permission to read).

Your program must not use a generic error message. Instead, use errno to implement a switch statement or a series of if checks to print a specific, custom message based on the error code. Specifically, handle these three cases differently:

  • ENOENT: Print "The file is missing from the disk."
  • EACCES: Print "You do not have the required permissions."
  • Any other error: Print "An unexpected system error occurred: [Insert strerror output here]."
Rating
0 0

There are no comments for now.

to be the first to leave a comment.