Don't use isdigit()
— it is for checking whether a character is a digit or not. You've read a number. You must check the return value from scanf()
— if it is not 1, you've got a problem. Depending on your requirements, the fact that there may be all sorts of stuff on the line after the number may or may not be a problem. I'm assuming when you say "validate the input is an integer", you want to allow for multiple-digit numbers, and negative numbers, and since you used %i
rather than %d
, you're fine with octal values (leading 0) or hexadecimal values (leading 0x or 0X) being entered too.
Note that if you have:
int checker = scanf("%i", &i);
then the result could be 1
, 0
, or EOF
. If the result is 1
, then you got an integer after possible leading white space, including possibly multiple newlines. There could be all sorts of 'garbage' after the number and before the next newline. If the result is 0
, then after skipping possible white space, including possibly multiple newlines, the first character that wasn't white space also wasn't part of an integer (or it might have been a sign not immediately followed by a digit). If the result is EOF
, then end-of-file was detected after possibly reading white space, possibly including multiple newlines, but before anything other than white space was read.
To continue sensibly, you need to check that the value returned was 1
. Even then, there could be problems if the value is out of the valid range for the int
type.
The full requirement isn't completely clear yet. However, I'm going to assume that the user is required to enter a number on the first line of input, with possibly a sign (-
or +
), and possibly in octal (leading 0) or hexadecimal (leading 0x or 0X), and with at most white space after the number and before the newline. And that the value must be in the range INT_MIN
.. INT_MAX
? (The behaviour of scanf()
on overflow is undefined — just to add to your woes.)
The correct tools to use for this are fgets()
or POSIX
getline()
to read the line, and
strtol()
to convert to a number, and isspace()
to validate the tail end of the line.
Note that using strtol()
properly is quite tricky.
In the code below, note the cast to unsigned char
in the call to isspace()
. That ensures that a valid value is passed to the function, even if the plain char
type is signed and the character entered by the user has the high bit set so the value would convert to a negative integer. The valid inputs for any of the ispqrst()
or topqrst()
functions are EOF
or the range of unsigned char
.
C11 §7.4 Character handling <ctype.h>
¶1
… In all cases the argument is an int
, the value of which shall be representable as an unsigned char
or shall equal the value of the macro EOF
. If the argument has any other value, the behavior is undefined.
The GNU C library tends to protect the careless, but you should not rely on being nannied by your standard C library.
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char line[4096]; /* Make it bigger if you like */
printf("Enter a number: ");
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "Unexpected EOF\n");
exit(EXIT_FAILURE);
}
errno = 0;
char *eon; /* End of number */
long result = strtol(line, &eon, 0);
if (eon == line)
{
fprintf(stderr, "What you entered is not a number: %s\n", line);
exit(EXIT_FAILURE);
}
if (sizeof(int) == sizeof(long))
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld: %s\n",
LONG_MIN, LONG_MAX, line);
exit(EXIT_FAILURE);
}
}
else
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld,\n"
"let alone in the range %d..%+d: %s\n",
LONG_MIN, LONG_MAX, INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
}
char c;
while ((c = *eon++) != '\0')
{
if (!isspace((unsigned char)c))
{
fprintf(stderr, "There is trailing information (%c) after the number: %s\n",
c, line);
exit(EXIT_FAILURE);
}
}
if (result < INT_MIN || result > INT_MAX)
{
fprintf(stderr, "What you entered is outside the range %d..%+d: %s\n",
INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
int i = result; /* No truncation given prior tests */
printf("%d is a valid int\n", i);
return(EXIT_SUCCESS);
}
That seems to work correctly for a fairly large collection of weird numeric and non-numeric inputs. The code handles both 32-bit systems where sizeof(long) == sizeof(int)
and LP64 64-bit systems where sizeof(long) > sizeof(int)
— you probably don't need that. You can legitimately decide not to detect all the separate conditions but to aggregate some of the errors into a smaller number of error messages.