【问题标题】:Code making an infinite loop when ask input with scanf [duplicate]使用scanf询问输入时生成无限循环的代码[重复]
【发布时间】:2019-10-06 03:56:14
【问题描述】:

我有一个函数要求用户输入一个值来计算其平方根,但是当我尝试验证输入的数字必须是数字而不是字符时,它会产生无限循环

void opcion1(void){
    float A, K, i, aux;
    int awnser;
    ask:
    fflush( stdin );
        printf("enter the value for A: ");
        sleep(1);
        awnser = scanf("%f", &A);
    if(awnser < 1){ // not a number
        fputs("\nA is not a number\n", stderr);
        goto ask;
    }
    if(A < 0 ){
        aux = -A;
        i = sqrt(aux);
        if(A == (int)A) printf("\nthe square root of %.0f, is%.0fi", A, i);
        else printf("the square root of %.3f, is %.4fi", A, i);
    }else{
        K = sqrt(A);
        printf("the square root of A is %.2f", K);
    }
}

输出:

enter the value for A:
k
A is not a number
enter the value for A:
A is not a number
enter the value for A:
A is not a number
enter the value for A:
A is not a number
enter the value for A:

【问题讨论】:

  • 你的意思是,如果你输入k,你会无限打印这两行?
  • 问题是fflush(stdin)。 C 标准只允许在输出流上使用fflush,而不是在输入流上。解决问题的一种方法是读取带有fgets的一行,然后使用sscanf解析数字。
  • 看看这个链接。 stackoverflow.com/a/1716066/9345287
  • 因为scanf()在输入缓冲区中留下未被接受的字符,如果您尝试读取数字并且输入中有非数字(例如字母或标点符号)缓冲区,你得到一个无限循环。测试scanf() 是否成功。如果没有,通常的技巧是使用像{ int c; while ((c = getchar()) != EOF &amp;&amp; c != '\n') ; } 这样的循环(所以有一个空循环体)。如果scanf() 报告 EOF,您的 shell 应该可能退出。

标签: c validation scanf goto


【解决方案1】:

主要问题是,当您刚开始使用该语言时,很难理解所有这些概念。就像scanf 到底在哪里接受输入?什么是缓冲?

我不会谈论技术术语,如果您想深入了解,可以随时在 Google 上搜索它们,并尽量保持简单。另外,我只是从 unix 方面谈论,对 windows 没有任何看法。

scanf(3) 在内部调用read(2)printf(3) 调用write(2)。那么我们为什么不直接使用read(2)write(2) 呢?问题是read(2)/write(2) 很慢,因为对他们来说,需要从用户模式切换到内核模式,这是一个耗时的过程。 scanf()printf() 所做的是它们保持在用户模式并在那里收集信息并一次性读取/写入它,即它们暂时将其存储在缓冲区中。

现在这让更多的事情成为可能。由于输入尚未到达您的变量并且在缓冲区中,scanf() 可以进行额外的检查。就像您的情况一样,检查输入是否符合您的格式说明符。如果不是,它不会将其分配给您的变量并返回。但是 scanf 不会清除接收输入的缓冲区。在scanf() 的情况下,当您的输入与%f 不同时,它返回 0 并且您的 if 条件使其上升并再次输入。但它从未清除缓冲区。所以缓冲区包含与以前相同的输入,它再次对其进行操作,再次导致相同的问题。这会无限地继续下去。

现在解决方法:

由于“标准”问题,这有点令人困惑。

当您看到fflush(3) 的联机帮助页时,

它说:

对于输入流,fflush() 会丢弃任何已从底层文件获取但尚未被应用程序使用的缓冲数据。

然后在页面底部,

标准没有指定输入流的行为。大多数其他实现的行为与 Linux 相同。

所以fflush(stdin) 在您的系统上似乎无法正常工作。 (就像它不在我身上一样)

已在许多问题中讨论了实现这一目标的最佳方法:

如何解决您的问题:

Scanf skips every other while loop in C

更好的方法:

Using fscanf() vs. fgets() and sscanf()

另一种方法是将fflush(stdin) 更改为fpurge(stdin)(它不便携也不标准)。

来自fpurge(3) 的联机帮助页,我看不到在任何地方讨论:

函数 fpurge() 清除给定流的缓冲区。为了 输出流这将丢弃任何未写入的输出。对于输入流 这会丢弃从底层对象读取但尚未读取的任何输入 通过 getc(3) 获得;这包括通过推回的任何文本 ungetc(3).

还可以看看为什么在这种情况下使用goto 不是一个好主意。 What is wrong with using goto?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-20
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    相关资源
    最近更新 更多