The Global Variable errno in C
errnois a global variable of typeintdefined in the header<errno.h>, used to signal errors in standard C library functions.- When a standard C library function encounters an error, it sets
errnowith a specific error code, which can be used to determine the nature of the error. - It is good practice to reset
errnoto0before calling a function that might fail, in order to be able to distinguish between an actual error and the previous value oferrno. - Some of the standard values of
errnoincludeEDOM(mathematical domain error) andERANGE(result outside the representable range). - Error handling through
errnoallows writing more robust and resilient code in C language.
The global variable errno
In the previous lesson we saw how to handle errors in C language using function return values. In this lesson we will explore another common technique for error handling in C, which is based on the use of the global variable errno.
The use of errno descends from the convention adopted in the UNIX operating system (and also Linux) where, whenever an error occurs in a system call, a global variable called errno is set with a specific error code. This code can then be used by the program to determine the nature of the error and act accordingly.
Here we will limit ourselves to explaining how errno works in C language and what values it can assume according to the C language standard, omitting, instead, the specific operating system implementations.
In general, errno is a global variable of type int defined in the header <errno.h> (Although it can also be defined as a macro, but this is an implementation detail). Some of the standard C library functions, like all mathematical functions, when they encounter an error, set errno with a value that represents the type of error that occurred. In other words, the error is not signaled through the function return value, but rather through the global variable errno.
Therefore, the general scheme for using errno is the following:
#include <errno.h>
/* ... */
/* 1. Reset errno to 0 before the function call */
errno = 0;
/* 2. Call the function that can fail */
value = function_that_can_fail(parameters);
/* 3. Check if errno has been modified */
if (errno != 0) {
// Handle the error based on the value of errno
} else {
// Proceed with normal processing
}
We notice two important details in this scheme:
-
There is no need to declare
errno, as it is already defined in the header<errno.h>.In fact, it is an error to redeclare it in your own code, as it could shadow the original definition and cause unexpected behavior.
-
The variable
errnois modified only when an error occurs.If the called function succeeds,
errnoremains unchanged. For this reason, it is good practice to reseterrnoto0before calling a function that might fail, in order to be able to distinguish between an actual error and the previous value oferrno.
For example, if we need to call two functions that can fail, we must reset errno before each call. Otherwise, we might confuse an error from the first function with an error from the second:
#include <errno.h>
/* ... */
errno = 0;
value1 = function_that_can_fail1(parameters1);
if (errno != 0) {
// Handle the error from function_that_can_fail1
}
errno = 0;
value2 = function_that_can_fail2(parameters2);
if (errno != 0) {
// Handle the error from function_that_can_fail2
}
No standard C library function sets errno to 0
It is important to note that no standard C library function sets errno to 0 on success. Therefore, it is the programmer's responsibility to reset errno to 0 before calling a function that might fail, if one intends to use errno to detect errors.
Example: Square Root
Let's see a practical example of using errno with the sqrt function from the standard C mathematical library, which calculates the square root of a number.
The sqrt function returns a value of type double, but if it is called with a negative number, it cannot calculate the real square root and sets errno to EDOM, which indicates a mathematical domain error.
Therefore, we can write a program that uses sqrt and handles the error through errno:
#include <stdio.h>
#include <math.h>
#include <errno.h>
int main() {
double number;
printf("Enter a number to calculate its square root: ");
scanf("%lf", &number);
errno = 0; // Reset errno before the call
double result = sqrt(number);
if (errno == EDOM) {
printf("Error: Cannot calculate the "
"square root of a negative number.\n");
} else {
printf("The square root of %.2f is %.2f\n", number, result);
}
return 0;
}
Note that to compile this program, it may be necessary to link the mathematical library using the -lm option with the GCC compiler:
gcc -o sqrt_example sqrt_example.c -lm
In the example above, if the user enters a negative number, sqrt will set errno to EDOM, and the program will print an appropriate error message. Otherwise, it will print the result of the square root.
Values of errno
The values that the global variable errno can assume are defined in the header <errno.h>.
In general, the UNIX (and Linux) operating system and the POSIX standard define several common error codes that are set in errno. The C standard, however, defines only some of them, leaving specific environments to implement additional error codes.
The standard values of errno defined in the C standard are constant macros, including:
EDOM: Is set when a mathematical operation receives an argument outside the valid domain (for example, calculating the square root of a negative number).ERANGE: Is set when the result of a mathematical operation is outside the representable range (for example, an overflow). For example, if we pass the value1000to theexpfunction, which calculates the exponential, the result will be too large to be represented as adouble, sinceis an extremely large number not representable by a variable of type double. In this case,errnowill be set toERANGE.
Many mathematical functions might encounter both errors above. Therefore, it is good practice to check both values of errno after calling a mathematical function.