Scanf在没有等待输入的情况下返回0。

3

我从未用过C语言编程,但今天我必须写一个小程序。这个程序非常简单 - 我想要把两个整数相加。
但是当我尝试检查输入是否为数字并且第一个scanf返回0时,第二个scanf也会立即返回0而不等待输入。
代码:

int main()
{
    int a = 0;
    int b = 0;
    printf("Number a:\n");
    if (scanf("%d", &a) != 1)
    {
        printf("Not a number. a=0!\n");
        a = 0;
    }
    printf("Number b:\n");
    if (scanf("%d", &b) != 1)
    {
        printf("Not a number. b=0!\n");
        b = 0;
    }
    printf("%d\n", a+b);
    return 0;
}

UV用于检查输入函数... != 1的返回值。许多问题都源于没有执行这个第一步。至少这篇文章做到了这个重要的步骤。 - chux - Reinstate Monica
5个回答

2
第一个 fscanf() 无法将输入转换为数字的输入仍然存在于标准输入缓冲区中,并导致第二个 fscanf() 失败。尝试丢弃有问题的输入并重新提示用户:
#include <stdio.h>

int main(void) {
    int a = 0;
    int b = 0;
    int c;
    printf("Number a:\n");
    while (scanf("%d", &a) != 1) {
        printf("Not a number, try again:\n");
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (c == EOF)
            exit(1);
    }
    printf("Number b:\n");
    while (scanf("%d", &b) != 1) {
        printf("Not a number, try again:\n");
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (c == EOF)
            exit(1);
    }
    printf("%d\n", a + b);
    return 0;
}

使用实用函数对代码进行分解可以使其更加清晰:

#include <stdio.h>

int get_number(const char *prompt, int *valp) {
    printf("%s:\n", prompt);
    while (scanf("%d", valp) != 1) {
        printf("Not a number, try again:\n");
        while ((c = getchar()) != EOF && c != '\n')
            continue;
        if (c == EOF)
            return 0;
    }
    return 1;
}

int main(void) {
    int a, b;

    if (!get_number("Number a", &a) || !get_number("Number b", &b)) {
         return 1;
    }
    printf("%d\n", a + b);
    return 0;
}

1
这是因为一旦第一个scanf()失败,很可能是由于匹配失败,导致匹配失败的输入仍然留在输入缓冲区中,等待下一次调用消耗。
因此,下一个scanf()调用也会立即尝试消耗位于输入缓冲区中的相同无效输入,而不是等待显式外部用户输入,因为输入缓冲区不为空。 解决方案:在第一个scanf()输入失败后,您必须清理输入缓冲区,例如while (getchar() != '\n');就可以完成工作。

可能还需要检查换行符。 - dbush
3
你需要一个稍微复杂一点的while循环,最好包装在一个函数里。当读取到EOF时,它才会停止读取,而不是在输入缓冲区为空时停止读取,否则它将被阻塞。这意味着用户必须输入ctrl-d(Unix shell)或ctrl-Z(Windows终端)或类似的内容才能结束。 - hyde
@hyde 是的,基本上你是正确的,我只是想表达这个想法而已。 - Sourav Ghosh
谢谢你的帮助!使用EOF版本程序卡住了,但是使用'\n'的版本可以工作! - Koli96

1

如果上一次输入有任何输入,那么它会接受并跳过用户的输入。在下一个scanf中,它会接受上一个scanf语句剩余的新行,并自动消耗它。这就是您代码中发生的情况。

  • You can clear previous input in stdin stream by using fflush(stdin); before starting your program.

  • This can also be solved by leaving a space before % i.e scanf(" %d",&n);,Here leaving whitespace ensures that the previous new line is ignored.

  • Or we can use getc(stdin) before calling any scanf statement, Sometimes it helps very much.

      int main()
      {
       int a = 0;
       int b = 0;
       printf("Number a:");
       //getc(stdin);
       if (scanf(" %d", &a) != 1)
        {
          printf("Not a number. a=0!\n");
          a = 0;
        }
       printf("Number b:\n");
       //getc(stdin);
       if (scanf(" %d", &b) != 1)
        {
         printf("Not a number. b=0!\n");
         b = 0;
        }
       printf("%d\n", a+b);
       return 0;
      }  
    

请阅读这个问题/回答,了解使用fflush(stdin)的危险性,然后考虑从您的答案中删除该建议。 - Adrian Mole
当然,我会阅读它。谢谢你的建议。 - Anantashayana

0

这是因为在C语言中输入和输出不同步。程序可以在用户输入之后输出一些行,而输入尚未被读取。请尝试运行此代码:

char token;

scanf("%c", &token);
printf("%c\n", token);
printf("line 1\n");

scanf("%c", &token);
printf("%c\n", token);
printf("line 2\n");

scanf("%c", &token);
printf("%c\n", token);
printf("line 3\n");

在一行中输入abc

你可以想象成有两个独立的控制台,一个用于输入,另一个用于输出。


例如,您想要为a输入asd,并为b输入3。在这种情况下,第一个scanf将找不到任何数字并返回0。但它也不会从输入中读取任何内容。因此,第二个scanf也会看到asd
如果a不是数字,则应输入行中剩余的所有字符,直到'\n'以清除输入(请参见@Sourav的解决方案)。

0

你可以使用字符串来完成同样的操作,而不需要使用scanf。只需将用户输入作为字符串获取,并将其转换为整数。然后将字符串再次转换为整数以检查它们是否相同。如果它们相同,则将a和b视为整数,否则执行你想要的操作(你需要包含string.h)。以下是可行的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
int a;
int b;
char str1[100];
char str2[100];

printf("Number a:\n");  
scanf("%s", &str1); //take input as a string
a = atoi(str1); //convert string to integer
snprintf(str2, 100, "%d", a); //convert integer back to string

//if the integer converted to string is not the same as the string converted to integer
if (strcmp(str1, str2)!= 0)
{
printf("Not a number. a=0!\n");
a = 0;
}
printf("Number b:\n");
scanf("%s", &str1);
b = atoi(str1);
snprintf(str2, 100, "%d", b);

if (strcmp(str1, str2)!= 0)
{
printf("Not a number. b=0!\n");
b = 0;
}
printf("%d\n", a+b);

return(0);
}

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