使用 scanf 询问输入时产生无限循环的代码

标签 c validation scanf goto

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

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:

最佳答案

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

我不会谈论技术术语,如果你想深入,你可以随时用谷歌搜索它们,会尽量保持简单。另外,我只是从 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) 的联机帮助页时,

它指出:

For input streams, fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.

但是在页面底部,

The standards do not specify the behavior for input streams. Most other implementations behave the same as Linux.

所以 fflush(stdin) 似乎在您的系统上不起作用。 (就像我的没有一样)

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

如何解决您的问题:

Scanf skips every other while loop in C

更好的方法:

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

另一种方法是将 fflush(stdin) 更改为 fpurge(stdin)(它不是可移植的,也不是标准的)。

来自 fpurge(3) 的联机帮助页,我没有看到任何地方对此进行讨论:

The function fpurge() clears the buffers of the given stream. For output streams this discards any unwritten output. For input streams this discards any input read from the underlying object but not yet obtained via getc(3); this includes any text pushed back via ungetc(3).

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

关于使用 scanf 询问输入时产生无限循环的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58254083/

相关文章:

印度用户长途拨号 (STD) 代码的 PHP 验证代码

c - 在 C 中使用 fscanf 跳过行的剩余部分

c - 使用模运算符 c 遍历数组

c - 指针减法,32位ARM,负距离报告为正

使用 C 计算并从字符串中获取整数

c - 将 int 的每个数字分配给 int 数组

c - 带冒号分隔符的 sscanf

c - C 中的多重定义

php - 如何防止 MySQL 使用空字符串更新列?

php - 在 Yii 的文本字段中显示错误消息