Error handling in C has always been the developer's responsibility. The standard library does not have exceptions; errors are returned via return codes or the global variable errno (first used in UNIX in the 1970s, then POSIX and ANSI C). Such mechanisms are still used today to manage the flow of execution in non-standard situations.
Errors when working with standard functions (file operations, memory allocation, string functions) can be unnoticed without special control. Incorrect handling — ignoring the return code, misinterpreting errno, failure to release resources — leads to incorrect program behavior, crashes, and vulnerabilities.
Proper error handling requires mandatory analysis of values returned by functions, using errno only immediately after a failure, and providing informative error outputs. Return codes are preferred for internal functions — they allow for handling without global side effects. errno is more often used with system calls and standard library functions. After each potentially harmful operation, the return is analyzed, and the global state (errno) should not be overwritten by intermediate calls.
Code Example:
#include <stdio.h> #include <errno.h> #include <string.h> FILE *open_file(const char *filename) { errno = 0; FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Error: %s ", strerror(errno)); } return f; }
Key Features:
Can errno be used for user-defined functions if you want to propagate errors upwards?
No, errno is intended only for standard library and system calls. It is global, can be overwritten at any point, and is unsuitable for your own functions.
Is it necessary to set errno before every function call?
No, but it is recommended to reset errno (e.g., to zero) before calls if you plan to analyze changes. Not every function changes errno on success, only on error.
errno = 0; ... call dangerous function ...
Can errno be trusted after any function?
Only for those functions that, according to the standard, explicitly set it on failure. Many standard library functions do not touch errno on success. Documentation is your friend.
Opening a file without checking the result, errors are not analyzed, the program behaves incorrectly when the file is missing:
Pros:
Cons:
After each critical function, the result is checked; in case of an error, a detailed message with strerror(errno) is output, execution correctly terminates:
Pros:
Cons: