5

New here and I have a very simple question. I am making a simple program in C that requires the user to enter a choice of what to do with a char. After they enter the result, the program goes back to the menu. However it seems to take some sort of ghost input as if the char has some unknown value. I need to set the char back to its default state.

Code:

/* display menu for user */
void menu() {

    printf("\n- - - Phone Book Database - - -\n");
    printf("\nSelect an action:\n\n");
    printf("\tc:\tCreate a database entry.\n");
    printf("\ts:\tSearch the database entries.\n");
    printf("\td:\tDelete a database entry.\n");
    printf("\tq:\tQuit program.\n\n");

    printf("Enter choice: ");
    menu_choice = getchar();

    if(menu_choice != 'c' && menu_choice != 's'
        && menu_choice != 'd' && menu_choice != 'q') {
            printf("\n\n\tInvalid choice.\n");
            menu();
    }
    //fflush(stdin);
}

Here is an example output:

- - - Phone Book Database - - -

Select an action:

    c:  Create a database entry.
    s:  Search the database entries.
    d:  Delete a database entry.
    q:  Quit program.

Enter choice: c

Enter name: test

Enter address: test

Enter number: 3

- - - Phone Book Database - - -

Select an action:

    c:  Create a database entry.
    s:  Search the database entries.
    d:  Delete a database entry.
    q:  Quit program.

Enter choice: 
    Invalid choice.

- - - Phone Book Database - - -

Select an action:

    c:  Create a database entry.
    s:  Search the database entries.
    d:  Delete a database entry.
    q:  Quit program.

Enter choice: q

Entering c as an input calls the following function

/* creates a new record in array */
void create_record() {
    char name[MAX];
    char address[MAX];
    int number;

    rec_num++; /* add 1 to marker for record placement */

    printf("\nEnter name: ");
    scanf("%s", name);

    printf("\nEnter address: ");
    scanf("%s", address);

    printf("\nEnter number: ");
    scanf("%d", &number);

    strcpy(record[rec_num].name, name);
    strcpy(record[rec_num].address, address);
    record[rec_num].number = number;
}
braX
  • 11,506
  • 5
  • 20
  • 33
James Manes
  • 526
  • 1
  • 11
  • 23
  • What do you do when 'c' is chosen? – Xymostech Dec 04 '12 at 16:59
  • @Xymostech When c is chosen it calls another function to create a database entry. I use an array of structs for each field. That works fine, but when going back to the menu() function it places something as an answer and renders "Invalid choice." as you can see. – James Manes Dec 04 '12 at 17:01
  • It looks to me like the other function isn't taking input correctly, so there is extra input left in the buffer and when you get back to the menu, something is already selected. Can we see the code for the function you talked about? – Xymostech Dec 04 '12 at 17:02
  • 5
    I'm almost sure you read the inputs in that other function with `scanf` - and that leaves the newline in the input buffer, so when `menu` is next called, `getchar()` gets the newline from the input buffer. – Daniel Fischer Dec 04 '12 at 17:05
  • @Xymostech Alright done. – James Manes Dec 04 '12 at 17:09
  • @DanielFischer Yes I did use scanf. I see what you mean. Good call! I'll go ahead and try a fix. – James Manes Dec 04 '12 at 17:10
  • Your program structure is very interesting. Are you sure you want `menu` to be a recursive function? – Pedro Lamarão Dec 04 '12 at 19:14
  • @PedroLamarão Uh well.. I just thought it would be a nice way to do it. I don't think it causes many issues. I could place an if statement in main after calling menu() to check for the desired result. Do you think that would be best? – James Manes Dec 04 '12 at 20:46
  • @JamesManes consider your last `fflush()` line, which in your example is commented out. That line would execute N times in a particular program run if `menu` were called N times. "main loop" type functions usually have these prologue and epilogue regions doing important things. – Pedro Lamarão Dec 04 '12 at 20:54
  • @PedroLamarão Oh I see. Yes I suppose it would be best to implement it in a different way. Come to think of it, I think doing it without recursion would make things more efficient. Less stack frames taking up memory space. – James Manes Dec 04 '12 at 21:02

4 Answers4

4

Looks like you have your answer already, and reviewing, it is correct; using getchar() will read one character from stdin

printf("Enter choice: ");
menu_choice = getchar();

When I get to this prompt at the console and type c<enter key> it's causing two char's to go to stdin: 'c' and '\n'

The first time getchar(); runs, it picks up just the 'c' leaving the newline character. On the second iteration the '\n' is picked up without waiting on anything from the user; thus is seems as though there is "ghost input".

I just wanted to point out, whenever you're getting input from the user and the results aren't what you've expected it doesn't hurt to just dump it back to verify what's happening, something like:

if(menu_choice != 'c' && menu_choice != 's'
    && menu_choice != 'd' && menu_choice != 'q') {
        printf("\n\n\tYou entered %c (%d).\n", menu_choice, menu_choice);
        printf("\tThat's an invalid choice.\n");
        menu();
}

Would have shown you:

You entered
 (10).
That's an invalid choice.

Between the fact it was on a different line, and checking that decimal result against an ASCII table you'd see that 1010 is the newline character, which could help lead you to a result.

Edit:

1 option for consuming a newline -

printf("what character?");
c = getchar(); // Get the character from stdin
getchar();     // consume the newline character that was left

a second option:

printf("what character?");
scanf("%c ", &c); // Get the character from stdin, note the space after the %c
                  // that will allow any special characters including \t and \n

If you just want to get the character and then end at & consume the '\n', you have to add and escape the newline char:

printf("what character?");
scanf("%c\\n", &c); 
Mike
  • 47,263
  • 29
  • 113
  • 177
  • Very helpful. It did not solve my problem but it did lead me to further understanding on how this works. Thank you! – James Manes Dec 04 '12 at 18:50
  • 1
    @JamesManes - Yeah, I didn't really mention *how* to fix it, just what was happening... In retrospect I added a few "quick" fixes for your interest (see the Edit to my post). The second option is the better one as it allows for more flexability – Mike Dec 05 '12 at 14:53
  • I looked into it and I started using the getchar() option. Using scanf("%c " &c"); failed. After hitting enter to make the selection it would just make a new line in the terminal. I could keep hitting enter over and over with nothing but new lines being outputted. – James Manes Dec 05 '12 at 16:27
  • @JamesManes ... are you using Visual Studio by any chance? – Mike Dec 05 '12 at 16:42
  • No I am not. I am using Chocolat text editor for OS X and compiling via GCC from terminal. – James Manes Dec 05 '12 at 16:43
  • 1
    @JamesManes - OK, regardless, try out that last edit on my post (the MS hack) and see if it works on your setup as well. I'd be interested to know. – Mike Dec 05 '12 at 16:46
  • Just tried it and it worked wonderfully. I will now be using this solution. Thank you! – James Manes Dec 05 '12 at 16:49
  • @JamesManes - Happy to help. You can feel free to "accept" this answer instead if you like the solution better than the first one. ;) – Mike Dec 05 '12 at 16:54
2

As others have pointed out, it's the newline character. Just add another

(void) getchar(); /* read and discard newline */

where ever you only want to read one character.

Arun Taylor
  • 1,574
  • 8
  • 5
  • Thank you very much. I added this at the end of each function called and it fixed my issue completely. It may not be the best way, but it works. I'll look for another solution but use this in the meantime. – James Manes Dec 04 '12 at 18:50
0

The return char '\n' is probably being left in stdin so that next time you come around it getchar() is fetching this.

Try looking at the value that is fetched with getchar() either with a debugger or just by printing it out.

It may be that getchar() isn't a great choice for your application and that scanf() would do a better job, that way all of stdin will be fetched and it should be empty next time you come around. Obviously you will need to provide scanf with more than a single char to write too, as in your other code.

You can then simply look at the first char in the array to get your char.

B_o_b
  • 459
  • 1
  • 3
  • 9
-1

A stray character, almost certainly '\n', is left on your input buffer after your input commands.

To remove it, you can add:

fflush(stdin)

...right before your getchar() call.

utopianheaven
  • 1,267
  • 7
  • 18
  • I tried that and it does not work. I hear using fflush(stdin) is very improper in situations like this anyhow. – James Manes Dec 04 '12 at 17:16
  • 1
    @JamesManes C99 7.21.5.2p2, fflushing anything other than an output stream, an update stream, or NULL, is undefined behavior, not just "improper". [Don't do it](http://stackoverflow.com/questions/2979209/using-fflushstdin). – WhozCraig Dec 04 '12 at 17:18
  • @WhozCraig Didn't know that it was undefined behavior. I'll keep that in mind. You'd be surprised how many people suggested it to me on other sites! – James Manes Dec 04 '12 at 17:29
  • @JamesManes It *may* work on your toolchain, but if it does it certainly isn't standard, certainly isn't portable, and *is* UB. Code to the standard and your programs will not only compile, but will *behave* predictably on *all* platforms boasting C-compilers that likewise conform to said-standard. – WhozCraig Dec 04 '12 at 17:34