I was testing some code earlier that looked like this:
char input;
int data;
while(scanf("%c %d", &input, &data) != EOF) {
puts("foo");
}
And when I read it with the following input:
c 8
c 10
c 15
c 18
^D
This was my output:
foo
foo
foo
foo
foo
foo
foo
foo
Although I was only really expecting to have foo
printed once; that is, once the EOF signal was sent. I also tried running it outside my IDE and manually in the terminal (because CLion has issues with EOF), and when I ran it manually through the terminal, foo would print once after the first input, and then double print each time after.
However, when I use printf
instead of puts
inside the loop, my code runs as expected. I want to know why puts was causing an issue here? I imagine it has something to do with the IO stream, but I am not exactly sure why this is happening.
Is this undefined behavior? If so, why is that happening?
EDIT: Someone in the comments said that they were not getting the same issue. I've attached a screenshot of my code terminal. I am also running gcc-9.4 if that makes any difference.
CodePudding user response:
The root of the problem is that %c
does not skip over leading whitespace.
Here's what's happening - your input stream contains the following sequence:
{ 'c', ' ', 8, '\n', 'c', ' ', 10, '\n', 'c', ' ', 15, '\n', 'c', ' ', 18, '\n' }
The first call to scanf
reads 'c'
into input
and 8 into data
and returns 2
(for two successful conversions and assignments). The next call to scanf
reads '\n'
into input
and tries to read 'c'
into data
- that's a matching failure, so data
isn't assigned. However, since '\n'
was successfully read into input
, scanf
returns 1
(which is not EOF
). The next call to scanf
reads ' '
into input
and 10
into data
.
Lather, rinse, repeat. Depending on where you are in the sequence, scanf
will return 2
, 1
, or 0
, none of which are the same as EOF
. This is why you get multiple foo
outputs per line, because you're not reading what you think you're reading.
There are two things you need to do to fix this:
Put a blank in front of the
%c
specifier - this will tellscanf
to skip over leading whitespace;Instead of testing against
EOF
, test against2
- you are expecting 2 successful conversions and assignments per input line:
while ( scanf( " %c %d", &input, &data ) == 2 )
puts( "foo" );
CodePudding user response:
Don't compare to EOF, count the number of valid conversions. In particular, on the input you show:
scanf("%c %d", &input, &data)
will read the first c
into input
, assign the value 8 to data
, and return 2. The next character in the input stream is \n
, so the second scanf
stores that in input
and is unable to get an integer value out of c
, so it does not write anything to data
and returns 1. The next call to scanf
writes c
to input
and sets data
to 10. Rinse and repeat.
You probably meant to write:
while(scanf(" %c %d", &input, &data) == 2) {
(note the space before the %c
).
CodePudding user response:
After the first line, "%c %d"
is consuming the prior line's '\n'
and then choke on 'c'
and an int
, necessitating another loop to process the line.
Use a leading space to consume whitespace.
Check results.
// while(scanf("%c %d", &input, &data) != EOF) {
int count;
while((count = scanf(" %c %d", &input, &data)) != EOF) {
printf("count:%d input:%c data:%d\n",
count, count >= 1 ? input : '?', count >= 2 ? data : -1);
}