Unclear about stdin input issue

425 Views Asked by At

I write a piece of code to help myself understand how things work when stdin and stdout involved.

Here is my code:

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

void prompt(){
    int i=0;
    printf("please select:\n");                  //string1
    printf("1.input\n2.print\n3.Exit\n");        //string2
    scanf("%d",&i);
    switch(i){
        case 1:
            printf("Data input!\n");
            break;
        case 2:
            printf("data printed!\n");
            break;
        case 3:
            exit(0);
        case 10:
            printf("Newline detected!");                      //string3
        default:
            printf("Please input a valid number!(1-3)");    //string4
    }
}   

int main()
{
    while(1)
        prompt();

    return 0;
}

What I expect this code to do is:

  1. prompt me for input;
  2. then I enter a number 4,which is out of the cases;
  3. so the default case will be matched and the string 'Please input a valid...'(string 4) will be printed.
  4. as there is still a newline character left in the stdin buffer, in the next loop, the variable 'i' will automatically get a newline character, which is 10 in ACSII
  5. So after printing out 'please select..1.input\n2.print....',, a string 'Newline detected!'(string 3) will be immediately printed out.
  6. And then the code goes into the third loop, and prompt me for input....

But that never happen. I cannot see any 'Newline detected!" in the output even if I enter a number 4, which is out of the cases.

Anyone can elaborate how this snippet work exactly?

By the way: I originally assume that when there is something printed in the stdout, the stdin buffer will be flushed automatically. But some fact proved me wrong. Is my assumption true or false?

Also, when I enter a character(for example, a g) rather than a number, I got string 1, string 2, sring 4 printed in the screen infinitly. Why is that?

#######################################################################3

Edit: After viewing the answer, I make another snippet, to help understand.

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

void output();

int main()
{
    int i;
    printf("Enter a number here:\n");
    scanf("%d",&i);

    output();

    return 0;
}

void output(){
    char a;
    if (scanf("%c",&a)!=1){
        printf("scanf error!!");
        exit(1);
    }
    switch(a){
        case 'a':
            printf("an char a is entered");
            break;
        case 'b':
            printf("an char b is entered");
            break;
        default:
            printf("%c",a);
            printf("other thing is entered");
    }
}

whatever you enter the first time the program prompt you, you will never get your second prompt. For example, when the program prompt you for the first time, if you enter a number 4, then you will get a newline and a string "other thing is entered" printed on you screen. Why is that?

3

There are 3 best solutions below

3
On

Addressing the second program in the question, here's a mildly revised version. It prints more information, and uses strict prototypes.

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

void output(void);

int main(void)
{
    int i;
    printf("Enter a number here:\n");
    if (scanf("%d", &i) == 1)
        printf("Read %d OK\n", i);

    output();

    return 0;
}

void output(void)
{
    char a;
    if (scanf("%c", &a) != 1)
    {
        printf("scanf error!!");
        exit(1);
    }
    printf("Character %d (%c) entered\n", a, a);
    switch (a)
    {
    case 'a':
        printf("an char a is entered\n");
        break;
    case 'b':
        printf("an char b is entered\n");
        break;
    default:
        printf("%c", a);
        printf("other thing is entered\n");
        break;
    }
}

Example runs:

$ ./stdin
Enter a number here:
23499911
Read 23499911 OK
Character 10 (
) entered

other thing is entered
$ ./stdin
Enter a number here:
2A
Read 2 OK
Character 65 (A) entered
Aother thing is entered
$ ./stdin
Enter a number here:
19b
Read 19 OK
Character 98 (b) entered
an char b is entered
$ ./stdin
Enter a number here:
999a
Read 999 OK
Character 97 (a) entered
an char a is entered
$

Note that in the first run, the stray character is the newline after the second 1 digit, character code 10. This is what you should get.

Do make sure your output print operations end with a newline (so that it appears in a timely manner). If you don't, your output may be held up indefinitely.

Do you mean that the scanf() function will not strip the newline character in the stdin buffer?

The scanf("%d", &i) certainly doesn't. The scanf("%c", &a) does. When scanf() completes a conversion, it puts the character that is not part of the conversion back into the input stream ready for the next input operation. So, it doesn't matter whether there's a space, a letter or a newline after the number, that character is left ready for the next input operation to read it. Most scanf() operations skip leading white space. There are three exceptions: %c, %n and %[…] (scansets). They do not skip leading white space.

3
On

as there is still a newline character left in the stdin buffer, in the next loop, the variable 'i' will automatically get a newline character, which is 10 in ACSII

Wrong. %d will get a integer until a newline or a space is encountered and the newline character in the buffer will not be consumed by the next scanf()

So always check the return value of scanf()

if(scanf("%d",&i) != 1)
{
 printf("scanf failed\n");
 return 1;
}

As a side note:

    case 10:
        printf("Newline detected!");

There is a a break missing in this case.

0
On

as there is still a newline character left in the stdin buffer, in the next loop, the variable 'i' will automatically get a newline character, which is 10 in ACSII

So after printing out 'please select..1.input\n2.print....',, a string 'Newline detected!'(string 3) will be immediately printed out.

That is not correct. When you use

scanf("%d",&i);

all white spaces are ignored, which includes the newline.

And then the code goes into the third loop, and prompt me for input....

Now you know it stays in the second loop, waiting for a number to be entered.

I originally assume that when there is something printed in the stdout, the stdin buffer will be flushed automatically. But some fact proved me wrong. Is my assumption true or false?

That assumption is false. stdout is flushed when you wait for input from stdin.

Also, when I enter a character(for example, a g) rather than a number, I got string 1, string 2, sring 4 printed in the screen infinitly. Why is that?

That's because the program is not able to read the character when it executes the line:

scanf("%d",&i);

The character stays in the input stream. You don't have any code to remove that character from the input stream. That makes the program stay in an infinite loop.