C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
120: Error Codes with errno.h
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
errnoto 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]."
There are no comments for now.