The scanf()
family of functions are a torture test that should not be inflicted on novice programmers. They're incredibly difficult to use accurately.
Analysis
Even ignoring the code layout (completely appalling — that's a professional assessment) and assuming you have variable declarations like this:
double value;
double *d = &value;
char newline;
char *c = &newline;
int val;
rather than the more plausible:
double d;
char c;
int val;
your fragment has an incredible number of problems crammed into a few lines.
while(val = fscanf(stdin,"%lf%c",d,c) != EOF)
{if(val==0){//error}
else if(c='\n'){//error print char}
else{//print double}
}
You have assigned the result of a comparison to val
, instead of assigning the return value of fscanf()
to val
and comparing that with EOF.
while ((val = fscanf(stdin, "%lf%c", d, c)) != EOF)
Now the code is merely wrong; fscanf()
could return EOF (usually, but not guaranteed to be, -1
), or 0
, or 1
or 2
, and of these, only 2
is the answer you're looking for. So, the loop condition should be:
while ((val = fscanf(stdin, "%lf%c", d, c)) == 2)
Or, if you've got the simpler definitions, then it should be:
while ((val = fscanf(stdin, "%lf%c", &d, &c)) == 2)
Now you know two values were read. The if (val == 0)
check inside the loop is now redundant; you only enter the loop body when val == 2
.
You can check whether the value in *c
is a newline doing a comparison instead of an assignment:
if (*c == '\n')
(or if (c == '\n')
; let's assume you have the simpler declarations and forgot the &
characters in the original code.) However, it's an error if c
is not a newline, so in fact it should be a !=
test.
while ((val = fscanf(stdin, "%lf%c", &d, &c)) == 2)
{
if (c != '\n')
...print error...
else
printf("Number = %g\n", d);
}
After the loop, you could test for val == EOF
vs val == 0
vs val == 1
. The first would mean you've reached EOF or an error occurred; the second would mean that the next character in the input is neither white space nor part of a valid number (a letter or a punctuation character other than .
, +
, -
, for example). The val == 1
case is esoteric; it would require that the end of the file occurs after a number that has no newline (or any other character) following it.
Infinite loop
The most likely cause of your infinite loop, incidentally, is an invalid (non-numeric, non-white space) character in the input. And, on closer inspection, I see that there's an r
on the second line of input; that would trigger an infinite loop with your code. fscanf()
would always return 0, which is not EOF, so the loop continues. (It can't read the r
with the %c
because it has to read a number successfully first.)
Synthesis
The %lf
format will skip any leading white space, including newlines, so your code is attempting to ensure that there is one number per line with no trailing white space. You'd probably do better in this context to use fgets()
and strtod()
, though using strtod()
completely correctly is not completely trivial. The following code ignores the finer points of strtod()
.
char line[4096];
double d;
while (fgets(line, sizeof(line), stdin) != 0)
{
char *end;
d = strtod(line, &end);
if (end == line || *end != '\n')
...incorrect format...
else
printf("Number = %g\n", d);
}
The only reasons for fgets()
to return a null pointer is that you've reached EOF or an error occurred. You might want to set errno = 0;
before the call to strtod()
and examine its value afterwards to spot numbers out of range.