3

I am creating a simple Tic Tac Toe for C, and here is a particular function which I am having a problem with. This is supposed to let the user select 'X' or 'O', and for the most art it works. However, if I enter a wrong symbol, it prints the statement: "Invalid symbol, please re-enter: " twice.

Why and how can I fix this?

char assign(void)                                 
{
      char user;

      printf("Would you like to be X or O (Enter your choice): ");
      user=getchar();
      while(user != 'X' && user != 'x' && user != 'O' && user != 'o')
      {
             printf("Invalid symbol, please re-enter: ");  
             user=getchar();
      }
      if(user == 'O' || user == 'o')        return('O');
      else if(user == 'X' || user == 'x')   return('X');     
}
Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
C_Intermediate_Learner
  • 1,800
  • 3
  • 18
  • 22
  • It probably catches the "\n" character when you hit enter. – Lucas Jul 27 '13 at 13:42
  • 1
    Possible duplicate of [Using getchar() in a while loop](http://stackoverflow.com/questions/2549701/using-getchar-in-a-while-loop). There was also [`while` loops duplicates `printf()` two times before getting to character](http://stackoverflow.com/questions/17892383/while-loops-duplicates-printf-two-times-before-getting-to-getchar) earlier today. There are likely to be others, too. – Jonathan Leffler Jul 27 '13 at 14:02

4 Answers4

4

The problem cause is related to the newline charachter

use scanf() in this way instead of using getchar()

scanf(" %c", &user);
MOHAMED
  • 41,599
  • 58
  • 163
  • 268
3

It's because when you use getchar it returns the next character, but leaves the newline in the input buffer. So the next getchar returns that newline.

You should also be careful because getchar actually returns an int and not a char.

You can solve this either by another getchar, or use scanf like this:

scanf("%c ", &user);

Note the space after the c in the above format, it tells scanf to read and disregard trailing whitespace.

You could also read a line with e.g. fgets and then use a simple sscanf on that line, then no extra space is needed.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

You could fix it like this for example:

char assign(void)
{
      char user;
      char throwaway_newline;

      printf("Would you like to be X or O (Enter your choice): ");
      user=getchar();
      throwaway_newline = getchar();
      while(user != 'X' && user != 'x' && user != 'O' && user != 'o')
      {
             printf("Invalid symbol, please re-enter: ");  
             user=getchar();
             throwaway_newline = getchar();
      }
      if(user == 'O' || user == 'o')        return('O');
      else if(user == 'X' || user == 'x')   return('X');     
}
Lucas
  • 13,679
  • 13
  • 62
  • 94
  • Once I do this, it doesn't repeat lines, but, it prints a weird 'u' symbol once I re-enter the valid symbol (FYI I added a break statement after `throwaway_newline = getchar();` – C_Intermediate_Learner Jul 27 '13 at 13:57
  • @C_Beginner_Learner: This is not a super great fix, to be honest (e.g. you cannot enter more than one symbol) I am not sure where your 'u' characters comes from. – Lucas Jul 27 '13 at 14:02
  • 1
    If a `break` is added in the loop and the character is incorrect, then you fall out of the bottom of the function without actually returning a value, so you might get `u` as the garbage non-returned value. The `break` (added by @C_Beginner_Learner) is inappropriate. The unchecked use of `getchar()` could cause problems; if you get EOF, you have an infinite loop. And the `else if` clause would probably be better as plain `else` or even without the `else`: `if (user == 'O' || user == 'o') return('O'); return('X');`. – Jonathan Leffler Jul 27 '13 at 14:09
  • but without the break, the statement "Invalid symbol, please re-enter:" continously repeats after entering symbols (as you said an infinite loop). So what can I do? @JonathanLeffler (besides changing the `else if` to else) – C_Intermediate_Learner Jul 27 '13 at 14:13
  • 1
    You can drive this code into a loop by typing just newline at the first response, or by typing blank and `o` (say) plus newline. The second iteration reads the newline into `user`, and then waits for more input. I think the simplest solution is to use `while (scanf(" %c", &user) == 1 && user != 'X' && user != 'x' && user != 'O' && user != 'o') { printf("Invalid symbol. Please re-enter O or X: ");` to read the character, and a loop `int c; while ((c = getchar()) != EOF && c != '\n') ;` afterwards to read up to and including the newline (also taking care of EOF). – Jonathan Leffler Jul 27 '13 at 14:26
  • @JonathanLeffler; Why you have used `scanf(" %c", &user) == 1` in while? Is this for checking that either user inputs a char or not or something else? – haccks Jul 27 '13 at 14:53
  • @haccks: With the `%c` format, it mainly detects EOF (when `scanf()` returns EOF) and handles that appropriately. With other formats, such as `%d` to read an integer, it detects both EOF and a complete mismatch, such as a letter, in the input. If you are looking for a number and the next character is a letter, `scanf()` will return 0 items converted. If you don't handle this, you can end up with an infinite loop (not so easily with `%c`). Input (in particular) requires constant care — always consider EOF and error conditions. – Jonathan Leffler Jul 27 '13 at 16:12
  • @JonathanLeffler; OK. I think `scanf(" %c", &user)` also return `1` if EOF is encountered, isn't it? – haccks Jul 27 '13 at 17:41
  • @haccks: No...go read the specification of [`scanf()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html). – Jonathan Leffler Jul 27 '13 at 17:42
1

You have a newline in your input buffer.

When you press a character which is not [xX] and not [oO] and follow it with a newline. getchar actually gets to see 2 characters(the newline and the invalid character)

You may want to use fgets instead of relying on character input and ignoring newlines with 2 getchar() calls everytime.

bsd
  • 2,707
  • 1
  • 17
  • 24