为什么scanf()会在此代码中导致无限循环?

45 c gcc scanf

我有一个小的C程序,它只是从stdin读取数字,每个循环周期一个.如果用户输入了一些NaN,则应该向控制台输出错误,输入提示应该再次返回.输入"0"时,循环应该结束,给定的正/负值的数量应该打印到控制台.这是程序:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,在输入一些非数字(如"a")时,这会导致无限循环一遍又一遍地写" - > Err ...".我想这是一个scanf()问题,我知道这个函数可以被更安全的替换,但这个例子适用于初学者,只知道printf/scanf,if-else和循环.

我已经阅读了这个问题的答案并浏览了其他问题,但没有真正回答这个具体问题.

jer*_*son 36

scanf仅使用与格式字符串匹配的输入,返回消耗的字符数.任何与格式字符串不匹配的字符都会导致它停止扫描并使无效字符仍在缓冲区中.正如其他人所说,在继续之前,仍然需要从缓冲区中清除无效字符.这是一个非常脏的修复,但它将从输出中删除有问题的字符.

char c = '0';
if (scanf("%d", &number) == 0) {
  printf("Err. . .\n");
  do {
    c = getchar();
  }
  while (!isdigit(c));
  ungetc(c, stdin);
  //consume non-numeric chars from buffer
}
Run Code Online (Sandbox Code Playgroud)

编辑:修复代码以一次性删除所有非数字字符.不再为每个非数字字符打印多个"Errs".

是一个非常好的scanf概述.

  • 现在,如果输入为"ab-10",它将错误地从输入中删除减号,并将"10"作为下一个数字. (2认同)
  • *返回消耗的字符数* -- 实际上,`scanf`返回成功读取的*字段*数。因此 `scanf("%d")` 可能返回 1、0 或 EOF。 (2认同)

Luc*_*cas 7

我想你只需要在继续循环之前刷新缓冲区.这样的事情可能会起作用,虽然我无法测试我在这里写的东西:

int c;
while((c = getchar()) != '\n' && c != EOF);
Run Code Online (Sandbox Code Playgroud)


Ted*_*ddy 6

scanf()将" a"仍留在输入缓冲区中以供下次使用.你应该用它getline()来读取一行,无论如何,然后用它strtol()或类似的方式解析它.

(是的,getline()是特定于GNU的,而不是POSIX.那么什么?问题是标记为"gcc"和"linux". getline()也是读取文本行的唯一合理选项,除非您想手动完成所有操作.)

  • 您不能依赖非标准扩展来处理与用户输入一样重要的事情,而不必在您自己的树中提供它们以防它们不存在.如果你编辑你的答案以反映这一点,我将撤回我的投票. (2认同)
  • @TimPost * getline() 和 getdelim() 最初都是 GNU 扩展。它们在 POSIX.1-2008 中标准化。* (2认同)