itsknob
itsknob

Reputation: 33

Two scanf statements inside a while loop not reading properly

First post here. I've been trying to write a very simple Knock Knock joke. It goes as follows:

Knock! Knock!

[user input]

Banana!

[user input]

Knock! Knock!

[user input]

Banana!

[user input]

... So on and so forth. It's a never ending joke. My problem comes from the scanf statements inside the while loop. If I type in 'a' for the first input nothing happens. If I then type ' a' (space in front), it comes up with Banana! If I type in 'a' for Banana!, it will continue the joke correctly.

However, if I type in "whos there" for the Knock! Knock! prompt it will go on to Banana! correctly; however, if I type in "banana who" for the Banana! prompt, if ends up saying "Knock! Knock!" and immediately "Banana!" not allowing input for the "Knock! Knock!" prompt the second time through the loop.

Here's a picture of those inputs: Picture

Am I missing something? I've tried adding getchar() statements. I've tried fflush(stdin). I've tried making the scanf a funtion and even adding a getchar() to that function. I've changed scanf("%s") to scanf("%s\n"), that helped but still doesn't function properly. It appears as though the second scanf is trying to take the whitespace as a terminating character and continue from there, but I am at a loss. I just want to make a simple never ending Knock Knock joke.

Am I just an idiot? What am I doing wrong?

Here's the code.

#include <stdio.h>

int main(void)
{
    while(1)
    {
        //Begin Joke
        printf("Knock! Knock!\n");
        scanf(" %s\n");                 //Who's there?

        //Second Part of Joke
        printf("Banana!\n");
        scanf(" %s\n");                 //Banana who?
    }
}

~Knob

Upvotes: 0

Views: 685

Answers (3)

chux
chux

Reputation: 153338

2 main problems: No place to save input and "\n" after "%s".

Use fgets() instead.


Let us tear scanf(" %s\n"); apart:

A. The " " directs scanf() to read and consume optional white-space (tabs, space, '\n', etc.). This continues until non-white-space is found. That char is put back into stdin.

B. "%s" directs scanf() to 1) read and consume optional white-space 2) read and save endless amounts of non-white-space in to oops a missing character array. 3) Upon encountering a white-space, put that char back into stdin, append a null character to oops a missing character array.

C. The "\n" directs scanf() to read and consume optional white-space (tabs, space, '\n', etc.). This continues until non-white-space is found. That char is put back into stdin. (Just like step A.)

So 1) we have undefined behavior because there is no character array to save data. 2) Even with a character array like below, scanf() does not return until non-white-space is entered (see step C.) after the the first run of non-white-space. Since stdin is line buffered, keyboard input occurs a line at a time. That 2nd word will not be seen by the first scanf() until 2 lines are entered.

char buf[100];
scanf(" %s\n", buf);  // better, but still not so good

If anything code could have been the below, but even that has issues as it does not consume a line of input.

char buf[100];
if (scanf("%99s", buf) != 1) Handle_Error();  // better

Instead use fgets()

char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_Error();  // best

// lop off potential trailing \n if desired
buf[strcspn(buf, "\n")] = '\0';

Upvotes: 1

Neijwiert
Neijwiert

Reputation: 1025

From cplusplus.com:

The additional arguments should point to already allocated objects of the type specified by their corresponding format specifier within the format string.

So provide it with an argument:

while (1)
{
    char buffer[255];
    //Begin Joke
    printf("Knock! Knock!\n");
    scanf(" %s\n", buffer);                 //Who's there?

                                    //Second Part of Joke
    printf("Banana!\n");
    scanf(" %s\n", buffer);                 //Banana who?
}

Please note:

This function is unsafe and like already mentioned may break. Again consider using fgets.

Upvotes: -2

user4520
user4520

Reputation: 3459

%s is not a good idea if you want to read entire lines; it'll normally break on any whitespace character. Some dirty hacks exist to circumvent this, but I suggest you just use fgets instead:

#include <stdio.h>

#define MAX_LINE_LEN 200

int main(void)
{
    char buf[MAX_LINE_LEN];

    while (1)
    {
        //Begin Joke
        printf("Knock! Knock!\n");
        fgets(buf, sizeof(buf), stdin);     //Who's there?

                                            //Second Part of Joke
        printf("Banana!\n");
        fgets(buf, sizeof(buf), stdin);     //Banana who?
    }
}

Output:

enter image description here


Note that what you were doing in your code (scanf(" %s\n");) is undefined behavior - %s requires a valid char buffer (of sufficient length!), but you were providing none. In general scanf is best avoided due to its poor error handling capabilities.

Upvotes: 4

Related Questions