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.
Viewing and Printing the Errno Value Your program can use the strerror() and perror() functions to print the value of errno. The strerror() function returns a pointer to an error message string that is associated with errno. The perror() function prints a message to stderr.
Since strtol() can legitimately return 0, LONG_MAX, or LONG_MIN (LLONG_MAX or LLONG_MIN for strtoll()) on both success and failure, the calling program should set errno to 0 before the call, and then determine if an error occurred by checking whether errno has a nonzero value after the call. According to POSIX.
To detect an error, an application must set errno to 0 before calling the function and check whether it is nonzero after the call. Affected functions include strcoll() , strxfrm() , strerror() , wcscoll() , wcsxfrm() , and fwide() . The C Standard allows these functions to set errno to a nonzero value on success.
So, you can reasonably safely assume that (system generated) error numbers are positive, but your code could set errno negative (or zero).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With