scanf被忽略,无限循环

3
int flag = 0;
int price = 0;
while (flag==0)
{
    printf("\nEnter Product price: ");
    scanf("%d",&price);
    if (price==0) 
        printf("input not valid\n"); 
    else 
        flag=1;
}

当我输入一个有效的数字时,循环按预期结束。但是如果我输入了不是数字的东西,比如hello,那么代码就会进入无限循环。它只会不停地打印输入产品价格:输入无效。但它不等我输入新的数字。为什么会这样?


编译器将您的程序转换为可执行文件,仅此而已。它与此处的行为无关。 - Jim Balter
6个回答

6
当你输入的不是一个数字时,scanf会失败并将那些字符留在输入中。所以如果你输入hello,scanf会看到h,拒绝它作为十进制数无效,并将其留在输入中。下一次循环时,scanf会再次看到h,因此它将一直循环下去。
解决这个问题的方法之一是使用fgets读取整行输入,然后使用sscanf解析该行。这样,如果sscanf失败,输入中就没有任何内容了。用户必须输入新行才能让fgets读取。
代码示例:
char buffer[STRING_SIZE];
...
while(...) {
    ...
    fgets(buffer, STRING_SIZE, stdin);
    if ( sscanf(buffer, "%d", &price) == 1 )
        break;   // sscanf succeeded, end the loop
    ...
}

如果你只是像另一个答案中建议的那样使用getchar,那么如果用户在数字后面输入了一些东西(例如空格,可能跟随其他字符),你可能会错过\n字符。
你应该始终测试sscanf的返回值。它返回分配的转换数,因此如果返回值与请求的转换数不同,则表示解析失败。在这个例子中,请求了1次转换,所以当sscanf成功时返回1。

3
"

%d格式用于十进制数。当scanf失败时(输入了除十进制数以外的内容),导致失败的字符将保留为输入。

例如。

"
    int va;
    scanf("%d",&va);
    printf("Val %d 1 \n", val);

    scanf("%d",&va);
    printf("Val %d 2 \n", val);
    return 0;

因此不会发生转换。

如果在任何转换之前发生输入失败,则scanf函数返回宏EOF的值。否则,scanf函数返回分配的输入项数,这可以比提供的少,甚至在早期匹配失败的情况下为零。

7.19.6. The scanf function - JTC1/SC22/WG14 - C

因此,您应该注意,scanf返回其自己的成功通知形式。

int scanf(char *format)

所以你也可以这样做。
do {
        printf("Enter Product \n");
}
while (scanf("%d", &sale.m_price) == 1);

if(scanf("%d", &sale.m_price) == 0)
        PrintWrongInput();

在编程中,记得尽量避免使用scanf。scanf或者扫描格式化的输入不应该用于交互式用户输入。请参考C FAQ 12.20

缓冲区中有一个 \n。那又怎样?除了%c,scanf会跳过空格。 - Jim Balter
1
@JimBalter 感谢您让我知道,我说了一个愚蠢的陈述:(。 - phwd

1
在第一个数字后,输入缓冲区中会有一个 '\n'(表示您按下回车键输入数字),因此在第二次迭代中,scanf 调用将失败(因为 \n 不是数字),scanf 不会从缓冲区中删除该 \n,因此在下一次迭代中它将再次失败,以此类推。
您可以通过在 scanf 后使用 getchar() 调用读取 '\n' 来解决这个问题。

2
如果用户在按回车键之前在数字后面输入了其他内容怎么办?请参见下面的另一个答案。 - ChrisJ
1
-1:scanf转换说明符"%d"会跳过输入开头的空格。如果您在第一个scanf中键入"43\n37",它将读取并转换"43",并将"\n37"留在缓冲区中。第二个scanf将读取并将其转换为37。肯定有额外的字符存在,但它们不是'\n' - pmg
最近为什么会出现这么多毫无头绪、懒得验证自己言论的人发布“答案”呢?而且为什么人们还会点赞这个明显错误的回答呢? - Jim Balter

1

那些认为缓冲区中包含'\n',因此会出现问题的“答案”是错误的 - scanf(“%d”,...)跳过空格,包括换行符。

如果x包含0,并且scanf遇到非数字字符(不仅仅是空格)或EOF,它将进入无限循环,因为x将保持为0,而没有办法使它变成其他值。只需要查看您的代码并考虑在这种情况下它会做什么就可以明显地看出这一点。


0

编辑:当我第一次写这个答案时,我对 scanf() 的工作原理非常愚蠢和无知。

  • 首先让我澄清一些事情,scanf() 不是一个有问题的函数,如果我不知道 scanf() 如何工作,也不知道如何使用它,那么我可能还没有阅读 scanf() 的手册,这不能归咎于 scanf()
  • 其次,为了理解代码中的问题,您需要知道 scanf() 的工作原理。

当您在代码中使用 scanf("%d", &price) 时,scanf() 尝试从输入中读取一个 整数,但如果您输入一个非数字值,scanf() 知道它不是正确的数据类型,因此将读取的输入放回缓冲区,在下一个循环周期中,无效输入仍然在缓冲区中,这将导致 scanf() 再次失败,因为缓冲区尚未被清空,这个循环会一直进行下去。

为了解决这个问题,您可以使用scanf()的返回值来处理它,该返回值将是成功读取的输入数,但是您需要通过刷新缓冲区来丢弃无效的输入,以避免无限循环。当按下enter键时,输入缓冲区被清空,您可以使用getchar()函数暂停获取输入,这将需要您按下enter键以丢弃无效的输入。请注意,无论您输入的数据类型是否正确,这都不会使您连续按两次enter键,因为newline character仍然在缓冲区中。在scanf()成功从输入中读取integer后,它会将\n放回缓冲区中,因此getchar()会读取它,但由于您不需要它,所以将其丢弃是安全的:
#include <stdio.h>

int main(void)
{
    int flag = 0;
    int price = 0;
    int status = 0;
    while (flag == 0 && status != 1)
    {
        printf("\nEnter Product price: ");
        status = scanf("%d", &price);
        getchar();
        if (price == 0) 
            printf("input not valid\n"); 
        else 
            flag = 1;
    }   

    return 0;
}

0

它进入了一个无限循环,因为如果匹配失败,scanf()不会消耗输入令牌。scanf()将一遍又一遍地尝试匹配相同的输入。你需要清空stdin。

如果(!scanf("%d", &sale.m_price)) fflush(stdin);


fflush(stdin) 是未定义行为。 - ilgaar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接