0

I am experiencing a strange behaviour of while loop in the following program.

#include <stdio.h>

int main ()
{

    char confirm;

    printf("Do you want to proceed? (Y/N):\n");
    scanf(" %c", &confirm);
    while (confirm != 'Y' && confirm != 'y' && confirm != 'N' && confirm != 'n')
    {
         printf("Please enter a valid answer (Y/N):\n");
         scanf(" %c", &confirm);
    }
 return 0;
}

Basically, I want the program only to accept 'Y', 'N', 'y' or 'n' characters. The above code works but when I enter a string as input, it prints "Please enter a valid answer (Y/N):" as many times as the characters of the string. Something like below:

Do you want to proceed? (Y/N):
a
Please enter a valid answer (Y/N):
adsfaf
Please enter a valid answer (Y/N):
Please enter a valid answer (Y/N):
Please enter a valid answer (Y/N):
Please enter a valid answer (Y/N):
Please enter a valid answer (Y/N):
Please enter a valid answer (Y/N):

How can I get rid of this. Can somebody help?

JohnP
  • 109
  • 1
  • 10

2 Answers2

2

scanf() takes the input that matches the format string, returning the number of characters consumed. Any character that doesn't match the format causes it to stop scanning and leaves the invalid character still in the buffer.

To let the strange not happen, the possible ways to fix this issues:

  1. Use fgets() to take the string and compare - recommended.
  2. Use fflush(stdin) - not recommended.

Aside: Use int confirm rather than using char for it. The reason is:

A char is required to accept all values between 0 and 127 (included). So in common environments it occupies exactly one byte (8 bits). It is unspecified by the standard whether it is signed (-128 - 127) or unsigned (0 - 255).

An int is required to be at least a 16 bits signed word, and to accept all values between -32767 and 32767. That means that an int can accept all values from a char, be the latter signed or unsigned.

Note: You may use char confirm when you specifically needs to store characters.

Reference: Difference between char and int when declaring character.

Example 1 (recommended):

#include <stdio.h>
#define MAX_LENGTH 100

int main(void) {
    char confirm[MAX_LENGTH];

    printf("Enter a valid command (Y/N): ");
    fgets(confirm, MAX_LENGTH, stdin); // using fgets() here

    while (confirm[0] != 'Y' && confirm[0] != 'y' \
           && confirm[0] != 'N' && confirm[0] != 'n') {

        printf("Please enter a valid answer (Y/N): ");
        fgets(confirm, MAX_LENGTH, stdin); // using fgets() here
    }

    return 0;
}

Expected sample output should be:

$ gcc -o prog prog.c; ./prog
Enter a valid command (Y/N): asdf
Please enter a valid answer (Y/N): asdlfasdf asdf a
Please enter a valid answer (Y/N): 234
Please enter a valid answer (Y/N): n

Example 2:

Do something like:

while (...)
{
     printf("Please enter a valid answer (Y/N):\n");
     fflush(stdin);
     scanf(" %c", &confirm);
}

Then you may expect this output:

Do you want to proceed? (Y/N):
d
Please enter a valid answer (Y/N):
asdf
Please enter a valid answer (Y/N):
asdfasd
Please enter a valid answer (Y/N):
n
Rohan Bari
  • 7,482
  • 3
  • 14
  • 34
  • The value of `confirm` is only used to compare with character constants. They should be pretty well in range for a `char` on any implementation. – Gerhardh Jun 19 '20 at 07:12
  • Also your linked reference tells: "If you want to store only characters in a variable, you should declare it as char. Using an int would just waste memory, and could mislead a future reader." What is the point of using `int` here? – Gerhardh Jun 19 '20 at 07:16
  • It's much better to simply read a line rather than flushing `stdin`, which is almost always a bad idea. – Tom Karzes Jun 19 '20 at 07:25
  • Well, I find it rather strange to suggest some change that does not apply to the question at all. It would apply if there was some `fgetc` involved. BTW: What will happen if you use `int` together with `%c` as you suggest? 3 bytes of the `int` will not be updated at all. – Gerhardh Jun 19 '20 at 07:27
  • @Gerhardh The point of answering a question on stack overflow is to show the best way to solve a problem. This question was about `fgets` in the sense that an experienced C programmer would recognize that's the best way to solve it. Often a literal answer to a specific question only serves to lead OP further down the wrong path, such as flushing `stdin`. Regarding the data type of `confirm`, you're right, it needs to be `char` if used as it is with `scanf`. With `getchar` you would use an `int`. – Tom Karzes Jun 19 '20 at 07:37
  • @TomKarzes I've updated the answer with an example of using `fgets()` to fix the problem. – Rohan Bari Jun 19 '20 at 07:48
  • @TomKarzes I fully agree. But the suggestions are not consistent. Suggesting `fgets` or `fgetc` is one thing. This is not part of the answer. (edit: *was* not part) Suggesting to use an `int` without adapting the usage accordingly is a different story. Using an `int` will leave part of the variable unspecified causing UB. – Gerhardh Jun 19 '20 at 07:48
  • @Gerhardh The suggestion to use an `int` was just a mistake. I already explained that in my previous comment, and I removed the comment in which I originally suggested the change. Why are you bringing it up again? – Tom Karzes Jun 19 '20 at 07:53
  • Besides that there is more UB: [using fflush(stdin)](https://stackoverflow.com/questions/2979209/using-fflushstdin) – Gerhardh Jun 19 '20 at 07:58
  • @TomKarzes I am not talking to your earlier comment below the question. I am talking about the suggestion in this answer. If you think the answer is good as it is, then there is nothing more to discuss. – Gerhardh Jun 19 '20 at 08:11
  • @Gerhardh You do understand that this is not my answer, right? This answer was posted by Rohan Bari, not me. – Tom Karzes Jun 19 '20 at 08:28
0

I tried the following. Don't know whether it is a good approach, but it works.

#include <stdio.h>

int main ()
{
    char confirm[256];

    printf("Do you want to proceed? (Y/N):\n");
    scanf("%256s", confirm);
    while (confirm[0] != 'Y' && confirm[0] != 'y' && confirm[0] != 'N' && confirm[0] != 'n')
    {
         printf("Please enter a valid answer (Y/N):\n");
         scanf("%256s", confirm);
    }

printf("%s\n", confirm);
}

give the output

Do you want to proceed? (Y/N):
somestring
Please enter a valid answer (Y/N):
234234
Please enter a valid answer (Y/N):
YES
YES
JohnP
  • 109
  • 1
  • 10
  • Edit: if the input is "some string" then line "Please enter a valid answer (Y/N):" appears two times. – JohnP Jun 19 '20 at 07:17