1

This is the first question I've posted about C programming on here as I just started learning C just a few weeks ago. Ill write up my code and ask what my problem is :) If Anyone please knows how I can fix my mistake or whatever I should replace for my code please reply:)!

The problem I am having, is that if you run the code for yourself, you will see that everything works fine, except for the 'else' part in the statement. The issue I am having is that when someone types more than one letter, it will run the last printf statement more than once, and will printf as many times as the user inputs a character other than y or n.

The first part with the Y or N is working fine, yet if they type any number of other chars, it doesnt just state "Please select again", one time and then re-scanf, it types out at least 2 printfs, just for even one character entered, "Please select again" "Please select again", and then, if you type more chars for the answer, it will just type even more "please select again"'s.

Please help me understand what I am doing wrong as I'm so keen on learning to program properly, but I am just stuck here atm :)

#include <stdio.h>
#include <conio.h>

int main()
{
    char answer;
    int loop = 0;

    printf("Please select. [Y/N]:\n");

    while (loop == 0)
    {
        scanf("%c", &answer);

        if (answer == 'y' || answer == 'Y')
        {
            printf("Seeyou Later Aligator.\n");
            break;
            return 0;
        }
        else if (answer == 'n' || answer == 'N')
        {
            printf("Mmkay.\n");
            break;
            return 0;
        }
        else
        {
            printf("Please select again [Y/N]:\n");
            loop = 0;
        }
    }
    getch();
    return 0;

}
zero323
  • 322,348
  • 103
  • 959
  • 935
Benjamin
  • 39
  • 3
  • 1
    `break;` followed by `return 0;` is kinda strange -- the return will never execute. – Hot Licks Oct 22 '13 at 04:05
  • 1
    And `loop` will never be anything other than zero. You could just as well say `while(TRUE)`. – Hot Licks Oct 22 '13 at 04:07
  • Your loop will only terminate if `loop != 0` or if a `break;` is encountered -- neither of those will happen if `answer` is none of `'n'`, `'N'`, `'y'`, `'Y'`. – alecbz Oct 22 '13 at 04:09
  • use fflush(stdin); at the end of while loop – 999k Oct 22 '13 at 04:09
  • 1
    What do you expect the loop=0 statement in the else part to do? You initialize loop=0 and then keep the loop going while loop==0 and then you set loop=0 in the else part - but that does nothing because loop is already zero. – Jerry Jeremiah Oct 22 '13 at 04:10
  • And study the [spec for scanf](http://www.cplusplus.com/reference/cstdio/scanf/). – Hot Licks Oct 22 '13 at 04:11

3 Answers3

3

scanf reads the required number of characters each time. If there are more characters, they are not ignored. They are read next time you call scanf. Hence you see multiple prints for every character. Inorder to explicitly ignore pending input, call fflush(stdin) after scanf. Which means to flush out any data in standard input stream.

Update: fflush should not be used on input streams as said in comments. Use the accepted solution for ignoring output. However I recommend using toupper or tolower instead of bit hack.

balki
  • 26,394
  • 30
  • 105
  • 151
  • fflush(stdin); doesn't seem to work ( i am using cygwin on windows ). if my input is `ff` output prints out 2 `printf` statements – sukhvir Oct 22 '13 at 04:24
  • @suhkvir Try putting `fflush` before `scanf` – balki Oct 22 '13 at 04:27
  • yeh i tried that .. won't work . here is a screen shot : http://i.imgur.com/7ariUsu.jpg – sukhvir Oct 22 '13 at 04:33
  • @sukhvir `fflush` is Microsoft specific. if you use with VS or turboC You will found its working. – Gangadhar Oct 22 '13 at 04:36
  • @Gangadhar i am using the basic cygwin setup on windows and my guess is OP is aslo using the same. I don't know what turboC is .. my guess is that OP doesn't know it either. ... With normal cygwin `fflush(stdin)` doesn't solve the problem (http://i.imgur.com/7ariUsu.jpg). I agree `fflush` is the best way to solve this .. but please mention how to use it in your answer to help out OP – sukhvir Oct 22 '13 at 04:40
  • 2
    fflush(stdin) is undefined behavior, fflush is supposed to work on output streams only. See the discussion here (http://stackoverflow.com/questions/2979209/using-fflushstdin). However, on Windows systems, it works. – Siddhartha Ghosh Oct 22 '13 at 04:41
  • @sukhvir OP using `getch();` a borland function. Mostly found on turboC ,please check my answer again, I was mention where to use `fflush()`. – Gangadhar Oct 22 '13 at 04:48
2

The reason as many have pointed out is that your scanf is reading the extra newline character left in the input buffer after the user presses ENTER. So here is an alternative way to read input to avoid that whole mess:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {

    char answer;
    printf("Please select. [Y/N]:\n");

    while (1)
    {

        scanf("%1s%*[^\n]", &answer);
        answer |= 0x20;

        if (answer == 'y')
        {
            puts("Seeyou Later Aligator.");
            break;
        }
        else if (answer == 'n')
        {
            puts("Mmkay.");
            break;
        }
        else
        {
            puts("Please select again [Y/N]:");
        }
    }

    getchar();
    return 0;
}

This will read just the first character found on stdin and ignore everything else after that and at the same time clear the input buffer of the newline character

smac89
  • 39,374
  • 15
  • 132
  • 179
  • elegant solution ... but it won't work for the very first input .. it will work after that. Also it will fail when 'y' is entered for the first time. Here is the screenshot: http://i.imgur.com/mAuvvL9.jpg – sukhvir Oct 22 '13 at 04:31
  • almost but still not 100% .. it doesn't recognise the correct input after wrong inputs . Here is the screen shot : http://i.imgur.com/hGw06af.jpg – sukhvir Oct 22 '13 at 04:53
  • Interesting, it seems to work for me. Here is [screenie](http://tinypic.com/r/2chxsog/5) – smac89 Oct 22 '13 at 05:04
  • hmmmm .. my point being .. your solution even though elegant doesn't seem to be universal ( different results on different platforms ) – sukhvir Oct 22 '13 at 05:07
  • 1
    You can tell I am just determined for this thing to work, what does the above one do on your system? It works for mine – smac89 Oct 22 '13 at 05:29
  • IT WORKS !!!! YAY!! .. now please explain this part : `scanf("%1s%*[^\n]", &answer); answer |= 0x20;' – sukhvir Oct 22 '13 at 05:31
  • The scanf part reads just one none whitespace character from stdin (idea from [here](http://personal.ee.surrey.ac.uk/Personal/R.Bowden/C/scanf.html)), and ignores everything else up to and including the newline character which is read and discarded. The `answer |= 0x20` is a bitwise operation done on char which will convert any uppercase or lowercase character to lowercase. The one for uppercase would be `answer &= 0x5f`. – smac89 Oct 22 '13 at 05:36
  • 1
    Damn .. This is a great solution – sukhvir Oct 22 '13 at 05:38
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/39704/discussion-between-smac89-and-sukhvir) – smac89 Oct 22 '13 at 05:39
  • @Smac89 Why `%1s` instead of `%c`? Why not use `toUpper` instead of a bit hack? – balki Oct 22 '13 at 15:51
  • @balki because `%1s` will ignore whitespace and newline thereby forcing the first input to be some other character. I use bit hack because function calls are expensive – smac89 Oct 22 '13 at 16:04
1
  1. break; is enough ... return will never be executed as you will break out of the while
  2. Its printing more than once because scanf is taking in '\n' and extra inputs from previous entry
  3. also the variable loop is pointless in your code

here is the fixed code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {

    char answer;

    int loop = 0;

    printf("Please select. [Y/N]:\n");

    while (1)
    {

        scanf("%c", &answer);

        if (answer == 'y' || answer == 'Y')
        {
            printf("Seeyou Later Aligator.\n");
            break;
            //return 0;
        }
        else if (answer == 'n' || answer == 'N')
        {
            printf("Mmkay.\n");
            break;
           // return 0;
        }
        else
        {
            printf("Please select again [Y/N]:\n");
            while(getchar()!='\n'){
                getchar();
                if(getchar() == '\n'){
                             break;
                }
            }

        }
    }

    getchar();
    return 0;
}

Output:

$ ./test
Please select. [Y/N]:
dddd
Please select again [Y/N]:
ffffff
Please select again [Y/N]:
y
Seeyou Later Aligator.
sukhvir
  • 5,265
  • 6
  • 41
  • 43