Detecting strtol failure [duplicate]
Solution 1:
The strtol
declaration in stdio.h
is as follows:
long int strtol(const char *nptr, char **endptr, int base);
strtol
provides a robust error checking and validation scheme that allows you to determine whether the value returned is valid
or invalid
. Essentially, you have 3 primary tools at your disposal. (1) the value returned, (2) the value errno
is set to by the call, and (3) the addresses and contents of nptr
and endptr
provided to, and set by, strtol
. (see man 3 strtol
for complete details - the example in the man
page also provides a shorter set of conditions to check, but they have been expanded below for explanation).
In your case you ask regarding a 0
return value and determining whether it is valid. As you have seen, a 0
value returned by strtol
does not mean that the number read was 0
or that 0
is valid. To determine if 0
is valid, you must also look at the value errno
was set do during the call (if it was set). Specifically, if errno != 0
and the value returned by strtol
is 0
, then the value returned by strtol
is INVALID. (this condition will represent either invalid base
, underflow
, or overflow
with errno
equal to either EINVAL
or ERANGE
).
There is a second condition that can result in strtol
returning an INVALID 0
. The case where no digits were read within the input. When this occurs, strtol
sets the value of endptr == nptr
. Therefore, you must also check whether the pointer values are equal before concluding a 0
value was entered. (a VALID 0
can be entered with multiple 0's
in the string)
The following provides a brief example of the differing error conditions to check when evaluating the return of strtol
along with several different test conditions:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main (int argc, char **argv)
{
if (argc < 2) {
fprintf (stderr, "\n Error: insufficient input. Usage: %s int [int (base)]\n\n", argv[0]);
return 1;
}
const char *nptr = argv[1]; /* string to read */
char *endptr = NULL; /* pointer to additional chars */
int base = (argc > 2) ? atoi (argv[2]) : 10; /* numeric base (default 10) */
long number = 0; /* variable holding return */
/* reset errno to 0 before call */
errno = 0;
/* call to strtol assigning return to number */
number = strtol (nptr, &endptr, base );
/* output original string of characters considered */
printf ("\n string : %s\n base : %d\n endptr : %s\n\n", nptr, base, endptr);
/* test return to number and errno values */
if (nptr == endptr)
printf (" number : %lu invalid (no digits found, 0 returned)\n", number);
else if (errno == ERANGE && number == LONG_MIN)
printf (" number : %lu invalid (underflow occurred)\n", number);
else if (errno == ERANGE && number == LONG_MAX)
printf (" number : %lu invalid (overflow occurred)\n", number);
else if (errno == EINVAL) /* not in all c99 implementations - gcc OK */
printf (" number : %lu invalid (base contains unsupported value)\n", number);
else if (errno != 0 && number == 0)
printf (" number : %lu invalid (unspecified error occurred)\n", number);
else if (errno == 0 && nptr && !*endptr)
printf (" number : %lu valid (and represents all characters read)\n", number);
else if (errno == 0 && nptr && *endptr != 0)
printf (" number : %lu valid (but additional characters remain)\n", number);
printf ("\n");
return 0;
}
output:
$ ./bin/s2lv 578231
string : 578231
base : 10
endptr :
number : 578231 valid (and represents all characters read)
$ ./bin/s2lv 578231_w_additional_chars
string : 578231_w_additional_chars
base : 10
endptr : _w_additional_chars
number : 578231 valid (but additional characters remain)
$ ./bin/s2lv 578some2more3stuff1
string : 578some2more3stuff1
base : 10
endptr : some2more3stuff1
number : 578 valid (but additional characters remain)
$ ./bin/s2lv 00000000000000000
string : 00000000000000000
base : 10
endptr :
number : 0 valid (and represents all characters read)
$ ./bin/s2lv stuff578231
string : stuff578231
base : 10
endptr : stuff578231
number : 0 invalid (no digits found, 0 returned)
$ ./bin/s2lv 00000000000000000 -2
string : 00000000000000000
base : -2
endptr : (null)
number : 0 invalid (base contains unsupported value)