If you type a letter instead of a number, scanf()
will return 0 (as in, "zero successfully converted numbers") and not EOF (as in, "there was no data left to read"). The correct test is to ensure that you got the expected number of values — in this case, 1:
while (scanf("%d", &grade) == 1)
If you need to know whether you got to EOF or got no result (but reading the rest of the line might clear the problem), then capture the return value from scanf()
:
int rc;
while ((rc = scanf("%d", &grade)) == 1)
{
}
if (rc != EOF)
…read the rest of the line, or at least the next character, before resuming the loop…
And, if you really want to, you could then write:
int rc;
while ((rc = scanf("%d", &grade)) != EOF)
{
if (rc == 1)
grades[gradesLength++] = grade;
else
{
printf("Discarding junk: ");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
putchar('\n');
if (c == EOF)
break;
}
}
The code in the else
clause could plausibly be put into a function. It might also report the messages to standard error rather than standard output. It is courteous to let the user know what it was that you objected to. You could stop before newline with a different test (&& !isdigit(c) && c != '+' && c != '-'
, using isdigit()
from <ctypes.h>
). However, the user doesn't have a chance to re-edit the stuff they put after the letters, so you may be going to misinterpret their input. It is probably better just to throw away the rest of the line of input and let them start over again.
As chux noted, after reading a character that could be part of an integer, that character needs to be put back into the input stream. Therefore, if I were going to analyze the rest of the line and restart scanning at the first data that could actually be part of an integer, I'd consider using something like:
#include <ctype.h>
static inline int could_be_integer(int c)
{
return isdigit(c) || c == '+' || c == '-');
}
and then the else clause might be:
else
{
printf("Discarding junk: ");
int c;
while ((c = getchar()) != EOF && c != '\n' && !could_be_integer(c))
putchar(c);
putchar('\n');
if (could_be_integer(c))
ungetc(c, stdin);
else if (c == EOF)
break;
}
This gets messy, as you can see. Sometimes (as Weather Vane noted in a comment), it is easier to read a line with fgets()
and then use sscanf()
in a loop (see How to use sscanf()
in a loop?). Be wary of suggestions about Using fflush(stdin)
; it isn't automatically wrong everywhere, but it won't work on a MacBook Pro under normal circumstances.
On the whole, simply ignoring the rest of the line of input is usually a better interface decision.