0

When compiling to Linux with gcc, everytime the user inputs whatever answer, the program reaches the same part of the code in other iteration but doesn't waits for the input of the user, and instead feeds a newline to the scanf function, making the program print 'I don't get it!' every time... The program works, but still, I didn't want it to write 'I don't get it!', independent of the users' answer. Here's the specific part of the code.

do
{
    printf("\n\nAnswer: (y/n) ");
    scanf("%c", &ans);
    //printf("\n->%c<-\n", ans); //just for debugging
    if (ans == 'y')
    {
        age += v[0];
    }
    else if (ans != 'n')
    {
        printf("\nI don't get it!\n");
    }
} while (ans != 'y' && ans != 'n');

These are the declarations:
char v[64];
char ans;

(This do..while loop is inside a 'for') What further puzzles me is the fact that the program compiles and runs exactly as I'd expect on Windows... (using MinGW) I've tried using fflush(stdin) before and/or after the scanf, but it didn't help. An important observation is that the first time it reaches this question it acts as expected.

(before the user answers)
Answer: (y/n)
I don't get it! // this gets written every time but the first

Answer: (y/n) n

You are 21 years old!

If the user writes invalid input:

Answer: (y/n) w

I don't get it!

Answer: (y/n) // this line and the next one should simply not be printed
I don't get it!

Answer: (y/n)
//(now it waits for user input)

Any ideas?

Edit

Fixed it: (I declared an additional char buf[50])

do
{
    printf("\n\nAnswer: (y/n) ");
    fgets(buf, 50, stdin);
    sscanf(buf, " %c", &ans);
    if (ans == 'y')
    {
        age += v[0];
    }
    else if (ans != 'n')
    {
        printf("\nI don't get it!\n");
    }
} while (ans != 'y' && ans != 'n');

Can someone tell me what is the problem with scanf?

agxxvi
  • 39
  • 6
  • There are lots of duplicates for this question/problem. The core issue is that the newline is left in the input buffer to confuse the next lot of input. Reading a line and then parsing that is usually the best way around the problem. I need to find a canonical duplicate for these questions; there must be a good one. – Jonathan Leffler Feb 13 '14 at 00:57

2 Answers2

1

Stop using scanf. Now.

Use fgets(buffer, BUFLEN, stdin) instead. Then use sscanf to parse the result. It will be more robust, and it will get rid of your problem.

I have heard it said that adding spaces around the format specifier can help too, because it gets rid of residual "whitespace". scanf(" %c", &ans); But I don't like it.

UPDATE here is a piece of code that I believe operates like you want it to (I replaced the innards with printf statements but you get the idea; I also replaced your ifs with a switch - I think it's cleaner):

#include <stdio.h>
#include <string.h>

#define N 100

int main(void) {

  char buf[N];
  char ans;
  int ii;

  for (ii = 0; ii < 10; ii++)
  {
    do
    {
      printf("\n\nAnswer: (y/n) ");
      fgets(buf, N, stdin);
      sscanf(buf, "%c", &ans);
      printf("==%c==", ans);
      switch(ans)
      {
      case 'y':
        printf("\nYou said yes :-)\n");
        break;
      case 'n':
        printf("\nYou said no :-(\n");
        break;
      default:
        printf("\nI don't get it!\n");
      }
    } while (ans != 'y' && ans != 'n');
  }
}
Floris
  • 45,857
  • 6
  • 70
  • 122
  • I had already tried using a lot of the input functions to get it working, and I'd already tried using fgets. For whatever reason, the program breaks when I use fgets. It starts looping infinitely, asking for the answer but writing 'I don't get it!' right away. – agxxvi Feb 13 '14 at 00:09
  • I wrote an example where using `fgets` doesn't "break the program". See if this inspires you to fix your code - hard to do when I don't have the whole thing. Good luck. – Floris Feb 13 '14 at 00:25
  • I think that writing fgets(&ans, 1, stdin), with ans as a single char caused the function not to behave as I had expected. Not sure why though. I'm pretty sure this code you made would work. Thanks for the help. – agxxvi Feb 13 '14 at 01:00
  • 1
    Well you can't safely write a one character string - the smallest size is 2 (character plus terminating `'\0'`) ; it is not enough to pass a pointer to `char` you need enough space (character, newline, terminating `'\0'`). That's why you were having trouble. Did my code work for you? – Floris Feb 13 '14 at 01:33
  • 1
    I didn't use this exact piece of code, I made a buffer to get the answer with fgets and then parsed the input with sscanf, as you had written earlier. Though I believe the switch statement is indeed cleaner, in this particular case I chose to write it with the 'if' because if the user says 'no' my program does nothing and simply loops. I thought it'd be awkward to write `case 'n': break;`. By using fgets and sscanf the code worked fine and I could compile to Linux and Windows without problems. – agxxvi Feb 14 '14 at 00:21
  • Happy it is working. A `switch` statement can have just `break` for specific cases (like "no" in your example). But that's just icing on the cake. Glad I could help! – Floris Feb 14 '14 at 01:46
0

Follow @Floris answer about fgets().


If you must use scanf(), use

scanf(" %c", &ans);

The space preceding "%c" will consume any and all leading white-space including the previous line's Enter or '\n'.

One certainly does not want to add a following space as that causes scanf() to consume all white-space until another non-white-space is entered. Since stdin is usually buffered, them means an additional '\n' after the non-white-space is needed for the line to be given to scanf().

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    The disadvantage of `" %c"` is that it requires some non-white space character (plus a newline if the input is coming from a terminal) to stop it, so just hitting return doesn't stop `scanf()`. That can be confusing too. I think that `fgets()` and `sscanf()` is 'the simplest sane way' to handle this sort of input. – Jonathan Leffler Feb 13 '14 at 01:00