Why can't you just check if errno is equal to ERANGE?

c, errno, strtol

Solution

The first code snippet is just plain wrong, and I'll explain why later, but first we need some background.

`errno` is a thread-local variable. It is set to a non-zero value when a system call or certain library functions fail. It remains unchanged when a system call succeeds. So it always contains the error number from last call that failed.

This means that you have two choices. Either set `errno` to 0 before each call, or use the standard idiom for `errno`. Here's the pseudo-code for the standard idiom

if ( foo() == some_value_that_indicates_that_an_error_occurred )
    then the value in errno applies to foo
else
    foo succeeded and the errno must be ignored because it could be anything

Most programmers will use the standard idiom, because setting `errno` to 0 before every system call is annoying and repetitive. Not to mention the fact that you might forget to set `errno` to 0 in the one place it actually matters.

Back to the first code snippet. It is wrong because there is no return value from `strtol` that unambiguously indicates that `strtol` failed. If `strtol` returns `LONG_MAX`, it could be that an error occurred, or the string actually contained the number `LONG_MAX`. There's no way to know whether the `strtol` call succeeded or failed. Which means that the standard idiom (which is what the first code snippet is trying to implement) cannot be used with `strtol`.

To use `strtol` correctly, you need to set `errno` to 0 before the call, like this

errno = 0;
result = strtol( buffer, &endptr, 10 );
if ( errno == ERANGE )
{
    // handle the error
    // ERANGE is the only error mentioned in the C specification
}
else if ( endptr == buffer )
{
    // handle the error
    // the conversion failed, i.e. the input string was empty,
    // or only contained whitespace, or the first non-whitespace 
    // character was not valid
}

Note that some implementations define other non-zero values for `errno`. See the applicable man page for details.

Problem

I've been trying to properly convert a char array to a long with `strtol`, check if there was an overflow or underflow and then do an int cast on the long. Along the way, I've noticed a lot of code that looks like this ``` if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) { // Handle the error } ``` Why can you not just say ``` if(errno == ERANGE) { // Handle the error } ``` From my understanding, if an underflow or overflow occur, errno is set to ERANGE in both cases. So is the former really necessary? Could checking ERANGE alone be problematic? This how my code looks as of now ``` char *endPtr; errno = 0; long result = strtol(str, &endPtr, 10); if(errno == ERANGE) { // Handle Error } else if(result > INT_MAX || result < INT_MIN) { // Handle Error } else if(endPtr == str || *endPtr != '\0') { // Handle Error } num = (int)result; return num; ``` If there is a reason for the former please let me know.

Original source

Related problems