【问题标题】:Unclear about stdin input issue不清楚 stdin 输入问题
【发布时间】:2015-06-20 14:36:56
【问题描述】:

我编写了一段代码来帮助自己理解当涉及stdinstdout 时事情是如何工作的。

这是我的代码:

#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;
}

我期望这段代码做的是:

  1. 提示我输入;
  2. 然后我输入一个数字 4,它不在案例中;
  3. 因此将匹配默认大小写并打印字符串“请输入有效的...”(字符串 4)。
  4. 由于标准输入缓冲区中仍有一个换行符,在下一个循环中, 变量 'i' 会自动得到一个换行符,在 ACSII 中是 10
  5. 所以在打印出“please select..1.input\n2.print....”后,会立即打印出字符串“检测到换行符!”(字符串 3)。
  6. 然后代码进入第三个循环,提示我输入......

但这永远不会发生。即使我输入数字 4,我也看不到任何“检测到换行符!”在输出中,这不在案例中。

谁能详细说明这个 sn-p 是如何工作的?

顺便说一句:我最初假设当标准输出中有打印的内容时,标准输入缓冲区会自动刷新。但一些事实证明我错了。我的假设是真的还是假的?

另外,当我输入一个字符(例如,g)而不是数字时,我在屏幕上无限打印了 string 1, string 2, sring 4。这是为什么呢?

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

编辑:查看答案后,我再做一个sn-p,以帮助理解。

#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");
    }
}

无论您在程序第一次提示时输入什么内容,都不会得到第二次提示。例如,当程序第一次提示您时,如果您输入一个数字 4,那么您将在屏幕上打印一个换行符和一个字符串“输入了其他内容”。这是为什么?

【问题讨论】:

    标签: c buffer stdout stdin


    【解决方案1】:

    由于stdin缓冲区中还有一个换行符,在下一个循环中,变量'i'会自动得到一个换行符,在ACSII中是10

    所以在打印出 'please select..1.input\n2.print....' 后,会立即打印出字符串 'Newline detected!'(字符串 3)。

    这是不正确的。当你使用

    scanf("%d",&i);
    

    所有空格都被忽略,包括换行符。

    然后代码进入第三个循环,提示我输入......

    现在您知道它停留在第二个循环中,等待输入一个数字。

    我最初假设当 stdout 中打印了某些内容时,stdin 缓冲区将自动刷新。但一些事实证明我错了。我的假设是真是假?

    这个假设是错误的。当您等待来自stdin 的输入时,stdout 会被刷新。

    另外,当我输入一个字符(例如一个 g)而不是一个数字时,我会在屏幕上无限地打印字符串 1、字符串 2、字符串 4。这是为什么呢?

    这是因为程序在执行该行时无法读取字符:

    scanf("%d",&i);
    

    字符留在输入流中。您没有任何代码可以从输入流中删除该字符。这使程序处于无限循环中。

    【讨论】:

      【解决方案2】:

      因为在标准输入缓冲区中还有一个换行符,在 下一个循环,变量 'i' 会自动换行 字符,ACSII中为10

      错了。 %d 将获取一个整数,直到遇到换行符或空格,并且缓冲区中的换行符不会被下一个 scanf() 消耗

      所以总是检查scanf()的返回值

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

      附注:

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

      在这种情况下缺少break

      【讨论】:

      • @walkerlala 您添加了对 scanf() 的检查,并且仅当 scanf() 通过时您才进入 switch()
      • 对不起。我不太明白你的意思。
      • 当我删除 if 测试时它是一样的。
      【解决方案3】:

      解决问题中的第二个程序,这是一个经过轻微修改的版本。它打印更多信息,并使用严格的原型。

      #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;
          }
      }
      

      示例运行:

      $ ./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
      $
      

      请注意,在第一次运行中,杂散字符是第二个 1 数字后的换行符,字符代码 10。这是你应该得到的。

      请确保您的输出打印操作以换行符结束(以便及时显示)。否则,您的输出可能会被无限期搁置。

      您的意思是scanf() 函数不会去除stdin 缓冲区中的换行符吗?

      scanf("%d", &amp;i) 肯定不会。 scanf("%c", &amp;a) 可以。当scanf() 完成转换时,它将不属于转换的字符放回输入流中,为下一次输入操作做好准备。因此,无论数字后面是否有空格、字母或换行符,该字符都已为下一次输入操作读取它做好准备。大多数scanf() 操作会跳过前导空格。有三个例外:%c%n%[…](扫描集)。它们不会跳过前导空格。

      【讨论】:

      • 你的意思是scanf()函数不会去掉stdin缓冲区中的换行符?
      • @walkerlala:scanf("%d", &amp;i) 肯定不会。 scanf("%c", &amp;a) 可以。当scanf() 完成转换时,它会将不属于转换的字符放回输入流中,为下一次输入操作做好准备。因此,无论数字后面是否有空格、字母或换行符,该字符都已为下一次输入操作读取它做好准备。大多数scanf() 操作会跳过前导空格。有三个例外:%c%n%[…](扫描集)。它们不会跳过前导空格。
      • 真的很有道理!!谢谢。而且我认为将您的回复放入您的答案中可能会很好
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-20
      相关资源
      最近更新 更多