Home > Blockchain >  C - Why does puts behave unexpectedly, but printf does not when using scanf?
C - Why does puts behave unexpectedly, but printf does not when using scanf?

Time:10-13

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.

enter image description here

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:

  1. Put a blank in front of the %c specifier - this will tell scanf to skip over leading whitespace;

  2. Instead of testing against EOF, test against 2 - 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);
}
  •  Tags:  
  • c
  • Related