【问题标题】:Enter a character as an input to an integer variable, giving an infinite loop [duplicate]输入一个字符作为整数变量的输入,给出一个无限循环[重复]
【发布时间】:2019-01-14 01:55:25
【问题描述】:
#include<stdio.h>
#include<ctype.h>
int main()
{
    int number, sum = 0;

    // loop body is executed at least once
    do
    {
        printf("Enter a number: ");
        scanf("%d", &number);
        sum += number;
    }
    while(number != 0);

    printf("Sum = %d",sum);

}

此代码应该打印用户输入的所有整数的总和。但是当用户输入一个字母作为输入时,它并没有抛出错误,而是进入了一个无限循环,打印了“输入数字”语句,但没有在循环中运行任何其他语句。

谁能告诉我为什么会这样?

【问题讨论】:

  • 使用scanf的返回值对你有利,而不是忽略它。
  • 你最好在这里使用strtol()
  • scanf 在遇到无效输入时不会“抛出错误”。它返回成功分配的接收参数的数量,您的工作是检查该返回值。
  • 为清楚起见,请在以后的问题中明确说明预期行为。你显然不想要一个无限循环。但是,您是否希望输入流中的错误数据被修剪掉,或者只是在错误输入时中断循环,这都留给了读者的想象。其中之一很简单(您选择的答案);另一个要复杂得多。除非你澄清你想要的行为,否则任何一个都是可以接受的答案。
  • 另一个重复的Why is scanf() causing infinite loop in this code?,至少还有50个。您可能还想仔细关注这里的讨论Is populating an integer array the same as populating a float one?

标签: c


【解决方案1】:

scanf() 不会丢弃与格式说明符不匹配的输入。您应该使用%s 在char[] 中读取它,然后执行atoi() 以尝试将其转换为字符串。如果失败,ERRNO 将被设置为一些错误代码。这样,如果输入的不是数字,您就不会留下任何未处理的输入。

【讨论】:

  • atoi 失败集errno ? Since when?
  • 内存有问题?如果你传入“abc”,它会做什么?返回 -1?
【解决方案2】:

对您的代码的适当修复将是下面的 sn-p。检查 scanf() 是否成功,然后继续

if (scanf("%d", &number) != 1)
{
        break;
}

【讨论】:

  • 这不是问题的答案(如果输入非数字,为什么会出现无限循环)。
  • 是的!!!!明白了,现在我很糟糕!
  • 可接受的解决方案,但在这种情况下,您的主循环(用于序列的输入)将在第一次错误输入后停止,而不是跳过错误输入并仅在输入0后停止@
  • 同意!为了实现这一点,有没有办法清除 scanf 的输入缓冲区?
【解决方案3】:

一种可能的解决方案是在 scanf 之后添加输入缓冲区清理代码,例如使用 getchar 循环:

#include<stdio.h>
#include<ctype.h>
int main()
{
    int number = -1; // any number not equal 0, to prevent loop break 
                     // after wrong input at the first iteration 
    int sum = 0;
    int c; // char to read input
    // loop body is executed at least once
    do
    {
        printf("Enter a number: ");
        if(scanf("%d", &number) == 1) {
            sum += number; // add if read was successful 
        }
        // buffer clean
        while ((c = getchar()) != '\n' && c != EOF);
    }
    while(number != 0);
    printf("Sum = %d",sum);
}

输入缓冲区清理的其他已知选项:

  1. fseek(stdin,0,SEEK_END);
  2. fflush(stdin);(对于某些编译器和操作系统,它可以工作)

【讨论】:

  • 您的 while 循环终止条件应包括 c 由于先前的尝试失败而到达 EOF。否则,你仍然有一个无限循环。一旦stdin 命中EOF,您将有效地跳过scanf(初始失败),并点击内部while,这将再次将c 设置为EOF。为此,这也可能将number 评估为不确定。如果 first scanf 失败 number 从未确定。这反过来又会调用未定义的行为
  • fflush(stdin) 是非标准的。该标准仅定义在输出流上调用的fflush 的行为。不会撒谎,我什至从未听说过有人试图在stdin 上执行fseek,但它包含已定义的行为,我会感到惊讶。
  • 如果if(scanf("%d", &amp;number)) 返回EOF,您将陷入无限循环。我建议if(scanf("%d", &amp;number) == 1)
  • @VolAnd 这正是他所说的情况,但总体上比这更糟。如果scanf 返回EOF ever,它将总是 这样做。尾随的刷新循环将是短暂的(实际上是一次迭代,重申 c 是 EOF),并且由于 number 没有改变,循环继续。
  • 那么让我换一种说法,OP的代码也被破坏了(显然,否则他们不会在这里)。他们没有考虑来自scanf任何返回结果。使用一个格式说明符,有三种可能的结果:0、1 或 EOF。解决方案是考虑到其中的所有。您的代码将后两者视为等效;他们不是。这是一个简单的解决方法,所以我不清楚你为什么不这样做,以便我可以提高你的答案。
【解决方案4】:

这个问题已经回答here

引用答案:

%d 转换说明符要求输入文本的格式为 一个十进制整数。如果不是,则转换失败并且字符 导致转换失败的内容留在输入流中。 使用 %d 转换说明符进一步调用 scanf() 将阻塞 在同一个字符上。

这基本上意味着当您使用%d 作为转换说明符调用scanf() 时,它会期望用户输入一个十进制整数。但是,当用户输入字符 (%c) 作为输入时,转换失败并且输入的字符保留在输入流中。这将导致 while 循环永远继续,而不允许用户输入更多输入。

为了防止这种情况,您可以尝试以下方法:

while (x != 0){
  int tmp;
  if (scanf("%d", &tmp) == 0)
    getchar();
  else
    number = tmp;
}

【讨论】:

    【解决方案5】:

    scanf 很奇怪。它有一个内部缓冲区,并且只有在该缓冲区为空时才会从流中读取。在代码的开头,什么都没有,所以从标准输入中读取一行。然后scanf 尝试在那里找到一个整数。如果找到一个整数,它将报告它成功读取了一项(通过返回1)并将读取的值放入提供的指针位置。但是,如果未找到整数,它将返回 0 以表示“成功读取零个项目”,并且它不会消耗缓冲区中的任何内容。这意味着,下次调用 scanf 时,缓冲区不会为空,因此不会再次提示用户换行,scanf 将再次尝试在与上次尝试完全相同的位置读取整数时间(并且会因为完全相同的原因而失败)。因此,无限循环。

    为了说明这个“缓冲”的东西,试试这个:

    scanf("%d", &a);
    scanf("%d", &b);
    printf("a: %d, b: %d\n", a, b);
    

    并在一行中输入12 53 ,作为第一个scanf 的输入。神奇的是,a12 结束,b53 结束。魔术的解释是 - 第一个 scanf 发现缓冲区为空,从用户那里读取该行,并找到一个整数,正如它被告知的那样;第二个scanf 发现缓冲区仍然存在一些东西,并继续从上次中断的地方读取,而无需询问用户换行。

    正如其他人已经评论的那样,通常最好自己阅读一行(fgets)并自己解析(strtol),或者如果您绝对需要使用scanf,请务必检查@987654340 @ 通过检查它的返回码来阅读你所期望的。如果scanf 失败,请参阅here 以了解如何清除输入缓冲区。

    【讨论】:

    • “内部缓冲区”本身并没有真正与scanf() 相关联,而是与scanf() 读取的stdin 流相关联。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-25
    • 2021-12-28
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多