Why can't you just check if errno equals ERANGE?

I am trying to correctly convert a char array to long with strtol , check if an overflow or underflow has occurred, and then do an int cast on long. Along the way, I noticed a lot of code that looks like

 if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) { // Handle the error } 

Why can't you just say

 if(errno == ERANGE) { // Handle the error } 

From my understanding, if an overflow or overflow occurs, errno is set to ERANGE in both cases. So what is the first really necessary? Can the ERANGE validation problem be problematic?

What does my code look like 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 first, please let me know.

+6
source share
2 answers

The first piece of code is simply incorrect, and I will explain why later, but first we need a little background.

errno is a local stream variable. If the system call or some library functions fail, a non-zero value is set. It remains unchanged when a system call is successfully completed. Thus, it always contains the error number of the last failed call.

This means that you have two options. 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 each system call is annoying and repeated. Not to mention that you can forget to set errno to 0 in one place, this is really important.


Return to the first code snippet. This is not true because there is no return value from strtol that uniquely points to strtol . If LONG_MAX returns LONG_MAX , an error may have occurred or the line really contained the LONG_MAX number. There is no way to find out if the call to strtol unsuccessful. This means that the standard idiom (which the first piece of code is trying to implement) cannot be used with strtol .

To use strtol correctly you need to set errno to 0 before calling 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, ie the input string was empty, // or only contained whitespace, or the first non-whitespace // character was not valid } 

Note that some implementations define other nonzero values ​​for errno . See the corresponding man page for details.

+5
source

If you call

 result = strtol("-2147483648", NULL, 0); 

or

 result = strtol("2147483647", NULL, 0); 

on a 32-bit machine, you will get LONG_MIN or LONG_MAX in result , although there was no error.

As user 3386109 explained, one way to detect errors from strtol is to set errno to 0. Another way is to give it an end pointer and look at it. There are three or four cases:

 char *endptr; long int result = strtol(str, &endptr, 10); if(*str == '\0') { /* str was empty */ } else if(endptr == str) { /* str was completely invalid */ } else if(*endptr != '\0') { /* numeric result followed by trailing nonnumeric character(s) */ } else { /* str was a completely valid number (perhaps with leading whitespace) */ } 

Depending on your needs, the first two or three cases can be rolled up together. Then you may need to worry: (a) whether a “fully real number” (which you can test with errno ) was representable and (b) whether any “terminating unnecessary characters (characters)” were harmless spaces (which, alas, strtol not check you, so if you do not care, you will have to check yourself).

+2
source

All Articles