Marlene said:
Suppose I'm using strtol() to convert a command line string
to a number and I want to check that the input to strtol() is
not non-numeric. strtol() returns zero if input is non-numeric,
so I can write something like this:
if((x = strtol(argv[1], NULL, 10))==0)
exit(EXIT_FAILURE);
That catches non-numeric input alright, but zero is a perfectly
good number that might be input to my program. How can I use
strtol() to convert zero while checking for non-numeric input?
It only catches some non-numeric input. For example, try
it on "99bottles" ...
strtol() uses three count them three channels to tell you
about conversion errors. First, there's the returned value:
zero if nothing could be converted, LONG_MIN or LONG_MAX if
the input was numeric or out of range. Of course, these three
values could also be returned by a successful conversion, as
you've observed.
If you get LONG_MIN or LONG_MAX, you can distinguish range
errors from legitimate conversions by setting `errno' to zero
before the call and checking whether it's been set to ERANGE
afterward. (Actually, I think Section 7.5 paragraph 3 of the
Standard implies that `errno' will only be set to ERANGE if an
error occurs, assuming it wasn't ERANGE already, so checking
for LONG_MIN and LONG_MAX might be avoidable. Gurus?)
Finally, you can distinguish "no conversion" from an actual
zero (and catch the "99bottles" problem) by making use of the
second argument, so strtol() can tell you where conversion
stopped.
All this rummaging around is inconvenient, and it's probably
best to write a wrapper function to shield yourself from the
unpleasantness. (Sad, isn't it, that a Standard library function
is so unpleasant -- but we are victims of "prior art" here.)
One simple-minded wrapper might take the string and a pointer
to a variable that receives the converted value, and return a
code indicating whether all went well, something like
#include <stdlib.h>
#include <limits.h> /* might not be needed */
#include <errno.h>
/* Returns 0 for success, -1 for failure */
int wrapper(const char *string, long *value) {
char *end;
errno = 0; /* anything other than ERANGE */
*value = strtol(string, &end, 10);
/* the first part of this test might be unneeded */
if ((*value == LONG_MIN || *value == LONG_MAX)
&& errno == ERANGE)
return -1; /* input out of range */
if (end == string)
return -1; /* no conversion possible */
if (*end != '\0')
return -1; /* trailing garbage */
return 0; /* success! */
}
Fancier wrappers are possible too, of course.