C Standard Library Errno H
# C Standard Library - ``
The `` header file in the C Standard Library provides a standardized mechanism for reporting and handling runtime errors. It defines the global/thread-local variable `errno` and a set of integer constants representing specific error codes. When a system call or library function encounters an error, it typically sets `errno` to a specific value indicating what went wrong.
---
## Introduction to `errno`
The `errno` macro expands to a modifiable lvalue of type `int`. It can be read and modified by the program.
Historically, `errno` was a global `int` variable. However, in modern multi-threaded environments, it is typically implemented as a thread-local variable (using Thread-Local Storage, or TLS). This ensures that error codes in one thread do not overwrite or interfere with those in another thread.
### How it Works
1. **Initial State**: At program startup, `errno` is initialized to `0`.
2. **Function Failure**: When a standard library function or system call fails, it sets `errno` to a non-zero value representing the specific error.
3. **No Reset on Success**: If a library function succeeds, it is **not** required to clear or reset `errno` to `0`. Therefore, you should only check `errno` after a function has returned a value indicating failure (e.g., returning `NULL`, `-1`, or `EOF`).
---
## Core Library Macros
According to the ISO C standard, `` must define at least the following three macros:
| Macro | Description |
| :--- | :--- |
| `errno` | A macro that expands to a modifiable lvalue of type `int`, representing the most recent error code. |
| `EDOM` | **Domain Error**: Occurs when an input argument to a mathematical function is outside the domain over which the function is defined (e.g., `sqrt(-1)`). |
| `ERANGE` | **Range Error**: Occurs when the result of a mathematical function cannot be represented by the return type due to overflow or underflow (e.g., `pow(10, 1000)`). |
---
## Common POSIX Error Codes
In addition to the standard C macros (`EDOM`, `ERANGE`), POSIX-compliant systems (like Linux and macOS) define a wide range of error macros in ``. Below are some of the most frequently used error codes:
* **`EPERM`**: Operation not permitted
* **`ENOENT`**: No such file or directory
* **`ESRCH`**: No such process
* **`EINTR`**: Interrupted system call
* **`EIO`**: Input/output error
* **`ENXIO`**: No such device or address
* **`E2BIG`**: Argument list too long
* **`ENOMEM`**: Out of memory / Cannot allocate memory
* **`EACCES`**: Permission denied
* **`EFAULT`**: Bad address
* **`EBUSY`**: Device or resource busy
* **`EEXIST`**: File exists
* **`EXDEV`**: Cross-device link
* **`ENODEV`**: No such device
* **`ENOTDIR`**: Not a directory
* **`EISDIR`**: Is a directory
* **`EINVAL`**: Invalid argument
* **`ENFILE`**: File table overflow
* **`EMFILE`**: Too many open files
* **`ENOTTY`**: Not a typewriter (inappropriate I/O control operation)
* **`ETXTBSY`**: Text file busy
* **`EFBIG`**: File too large
* **`ENOSPC`**: No space left on device
* **`ESPIPE`**: Illegal seek
* **`EROFS`**: Read-only file system
* **`EMLINK`**: Too many links
* **`EPIPE`**: Broken pipe
---
## Code Examples
### Example 1: Handling File I/O Errors
This example demonstrates how to check `errno` when `fopen` fails, and how to use `strerror` from `` to print a human-readable error message.
```c
#include
#include
#include
int main() {
// Attempt to open a file that does not exist
FILE *file = fopen("nonexistent_file.txt", "r");
if (file == NULL) {
// fopen failed, errno has been set to the appropriate error code (likely ENOENT)
printf("Error opening file: %s (Error Code: %d)\n", strerror(errno), errno);
return 1;
}
// File operations go here...
fclose(file);
return 0;
}
```
### Example 2: Handling Mathematical Domain and Range Errors
This example demonstrates how mathematical functions set `errno` to `EDOM` or `ERANGE`.
```c
#include
#include
#include
#include
int main() {
// Reset errno to 0 before the operation
errno = 0;
// Domain Error: Square root of a negative number
double val = sqrt(-1.0);
if (errno == EDOM) {
printf("Domain Error occurred: %s\n", strerror(errno));
}
// Reset errno to 0 before the next operation
errno = 0;
// Range Error: Exponential overflow
double val2 = exp(1000.0);
if (errno == ERANGE) {
printf("Range Error occurred: %s\n", strerror(errno));
}
return 0;
}
```
---
## Best Practices and Considerations
### 1. Thread Safety
In modern C (C11 and later) and POSIX environments, `errno` is thread-safe. It is implemented as a thread-local variable, meaning each thread has its own local copy of `errno`. You do not need to worry about race conditions on `errno` when multiple threads are executing system calls simultaneously.
### 2. Always Reset `errno` Before Certain Calls
Because successful library calls do not clear `errno`, you should manually set `errno = 0` before calling functions where checking `errno` is the only reliable way to detect an error (such as `strtol` or `strtod`).
```c
errno = 0;
long val = strtol(str, &endptr, 10);
if (errno != 0) {
// An overflow or underflow error occurred
}
```
### 3. Check Return Values First
Never rely on the value of `errno` unless the function's return value explicitly indicates that an error occurred. If a function succeeds, the value of `errno` is undefined and may contain leftover error codes from previous failed operations.
### 4. Do Not Rely on Specific Integer Values
Always use the symbolic macros (like `EINVAL`, `ENOMEM`, `EDOM`) instead of their raw integer values (like `22`, `12`, `33`). The actual integer values assigned to these macros can vary across different operating systems and CPU architectures.
YouTip