Why does my simple while loop printf 2x?

Discussion in 'C Programming' started by DFS, May 15, 2014.

  1. DFS

    DFS Guest

    char c;
    printf("Enter Y or N: ");
    c = toupper(getchar());
    if (c == 'Y' || c == 'N') {break;}
    return c;

    If I enter Y or y or N or n it works.

    If I enter any other letter it says:

    Enter Y or N: Enter Y or N:

    (that's 2x) and waits for input.

    Thanks for help!
    DFS, May 15, 2014
    1. Advertisements

  2. DFS

    BartC Guest

    What did you expect it to do when you enter anything other than Y or N?

    You've told it to repeatedly prompt for input until someone enters Y or N!

    You might add this at the end of the loop (before the }):

    printf("\nInput error.\n");
    BartC, May 15, 2014
    1. Advertisements

  3. DFS

    Fred K Guest

    You entered two characters (some character plus a carriage return)
    The first getchar() returns the character you entered; it fails your test, so continues the loop and prints the query.
    then getchar() reads the carriage return. That too fails the test, and prints the query again.
    Fred K, May 15, 2014
  4. DFS

    DFS Guest

    I expect it to stay in the While loop until Y or N is entered.


    But my question still stands: why does it prompt 2x every time I enter
    something other than Y or N?

    This is what happens when I do that:

    Input error.
    Enter Y or N:
    Input error.
    Enter Y or N:

    Thanks for your help, BartC
    DFS, May 15, 2014
  5. DFS

    DFS Guest

    Gotcha. Nice explanation.

    DFS, May 15, 2014
  6. DFS

    BartC Guest

    OK I misunderstood. I thought you meant the old and new prompts together
    making a double prompt.

    This is because you're doing line input so you end up getting two
    characters. So if someone types:


    then you will read 'A' followed by '\n'.

    You just have to keep reading characters until you skipped the '\n'
    (remembering someone might type a whole line of text).
    BartC, May 15, 2014
  7. DFS

    BartC Guest

    BTW if you have the getch() or _getch() function available, then you might
    consider using that. (I probably assumed you were, I didn't look closely at
    the char input line.)

    This lets you type Y or N with a single key-press without needing to press
    Enter (and you wouldn't have had that problem).

    getch() should be on Windows, while there are also versions available (as a
    fragment of source to download) for Linux.
    BartC, May 15, 2014
  8. DFS

    DFS Guest


    With yours and FredK's help, I changed it slightly and it almost works

    char c;
    printf("Enter Y or N (not case-sensitive): ");
    while(c != '\n')
    c = toupper(getchar());
    if (c == 'Y' || c == 'N') {break;}
    return c;

    No more double prompts, but letters other than y/n return the \n (shows
    as blank onscreen).

    So, how do I read just the nth character from getchar()?
    DFS, May 15, 2014
  9. DFS

    BartC Guest

    It's not clear what you're trying to do.

    At the moment this function returns Y or N when someone types a line
    containing Y or N (so 'xyz' returns 'Y', and 'anything' returns 'N'). And it
    returns \n for an empty line or one not containing Y or N (because that is
    the value of c at the end of the while loop).

    You need a better specification for this function.

    For example, should it only ever return Y or N when the line starts with
    those, and stay in the function until the user does that?
    BartC, May 15, 2014
  10. DFS

    DFS Guest

    I might like getch(), if I can get it to work.

    I added

    #include <conio.h>

    but gcc threw a fatal error:
    conio.h: No such file or directory

    Here's the entire, simple program.

    #include <stdio.h>

    int toupper(int c);
    int getch(void);

    char getLetter()
    char c;
    printf("Enter Y or N (not case-sensitive): ");
    while(c != '\n')
    c = toupper(getch());
    if (c == 'Y' || c == 'N') {break;}
    return c;

    int main(int argc, char *argv[])
    printf("You entered: %c\n", getLetter());
    return 0;

    gcc says:
    /tmp/cc6dRK3Z.o: In function `getLetter':
    getLetter.c:(.text+0x29): undefined reference to `getch'
    collect2: error: ld returned 1 exit status

    Thanks for your help
    DFS, May 15, 2014
  11. DFS

    DFS Guest

    I see that now!

    Ideally, I want the user to enter Y or N (upper or lower case).
    Anything else and they keep getting prompted for Y or N.
    DFS, May 15, 2014
  12. DFS

    BartC Guest

    If you're using Linux, then getch() doesn't officially exist. But google for
    'linux getch' and there should be some code which will emulate it (probably
    also kbhit(), another useful function).
    BartC, May 15, 2014
  13. DFS

    BartC Guest

    The following code does something along those lines. But even that is not
    ideal, because someone can type anything that happens to start with Y or N,
    and it will apparently work. And it won't accept a space before Y or N.

    Getting this stuff to work properly is always fiddly. A solution using
    would be simpler. But using line-oriented input, you should really be using
    line-processing routines, where you can read a whole word, so that you can
    accept 'y', 'n', 'no', 'yes' and reject anything else.

    void skiptoeol(void){
    while (getchar()!='\n') {}

    int confirm(void) {
    char c,d;
    while (1) {
    printf("Enter Y or N (not case-sensitive): ");
    if (c == 'Y' || c == 'N') {
    return c;

    int main(void) {
    char c;


    BartC, May 15, 2014
  14. getch() is a Windows-specific function that behaves as you describe.

    In the UNIX/Linux world, getch() is the name of a function provided by
    the curses/ncurses package, and it behaves quite differently.

    You could write a function that reads a single character without needing
    to press Enter, but (a) its implementation wouldn't be portable to
    non-POSIX systems, and (b) I would advise against calling it "getch".

    (And for the current purpose, line-buffered input is probably good
    enough anyway, you just have to deal with it properly.)
    Keith Thompson, May 15, 2014
  15. DFS

    BartC Guest

    I was thinking of a version that emulates Windows' getch(), for example the
    code here:


    I wouldn't get involved in curses/ncurses unless absolutely necessary.
    BartC, May 15, 2014
  16. That's a slightly odd file. It's a .c file, but it defines the two key
    functions (or procedures for those who use M-speak) as static.

    There still seem to be quite a lot of tutorials or books or courses or
    whatever that use this MSDOS-era style of interaction, so a slightly
    more polished unix/linux resource would be useful. For example, it
    should include a prototype for memcpy (or use an assignment), and it
    prints error messages on stdout, rather than stderr.

    Ben Bacarisse, May 16, 2014
  17. DFS

    DFS Guest

    Thanks. I'll try it tomorrow.
    DFS, May 16, 2014
  18. DFS

    BartC Guest

    I didn't notice anything about it, other than that it worked!

    The static functions sort of make sense, if they will only be used in the
    module that includes that file (and won't interfere with any other uses of
    kbhit/getch in the project).
    Looking at this line now:

    memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));

    I was thinking how much neater it would be if C allowed you to simply use an

    new_kbd_mode = old_kbd_mode;

    then realised that it does! (Maybe the original author wasn't aware of it or
    automatically used memcpy for any kind of block copy.)
    BartC, May 16, 2014
  19. But then your are teaching (surely people will use this only when
    leaning) including a .c file with function definitions. This is not
    good style. If it were my class, I'd write a conio.h and provide a
    "compile" command that silently includes the conio.o module (or a
    makefile of whatever).

    If you can't do that, I don't think its hard to tell people to "gcc -c
    conio.c" once and to tack conio.o onto their compile commands.

    I like to have people, when learning, turn up compiler warnings to
    SCREAM AND SHOUT levels, and you can get annoying warnings for every
    compile if you include a .c file that defines unused static functions
    (or indeed anything else). You don't want warnings about code the
    student hasn't written.
    Some things get stuck as idioms. Copying and zeroing OS control
    structures using memcpy and memset seem to be one of these. You see it
    all the time. It may date back to 1970's C when there was no struct
    assignment, or it may be an overly cautious hang-over from OS calls that
    have variable length structures.

    The code is good, but would be properly useful with just a tiny bit of
    tidying up and a README. People could then run old tutorial code on
    free OSes unaltered.
    Ben Bacarisse, May 16, 2014
  20. DFS

    BartC Guest

    I'm not teaching anything. I suggested to the OP that getch() might be
    another approach to his/her task, and to search for a Linux version if
    necessary to try it out.

    I didn't specify that particular one (except as an example to Keith), nor
    how to use it.

    (When I tested it, I think I just stuck a main() function at the end. But
    the relevant code can be used in many ways including extracting it to use in
    one's own library. Maybe even creating a conio.c, but I'm not familiar with
    what else is expected to be in there.)
    I use function definitions inside an include file from time-to-time. But
    then I'm using it as an actual include file and not a header file. C doesn't
    really distinguish between them.
    BartC, May 16, 2014
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.