5

我正在尝试:- 如果用户输入无效值,则重新读取该值。但问题是scanf()只执行一次并且不会执行任何其他时间并且程序陷入无限循环。

#include<stdio.h>
#include<math.h>
main()
{
    unsigned int a;
    unsigned int b = pow(2,M-1);
    unsigned int c;
    int x;

    printf("b = %i",b);

    input:
    fflush(stdin);
    fflush(stdout);
    printf("\nEnter any integer: ");
    x = scanf("%u",&a);

    printf("%u",a);
    if(x==0) 
        goto input;

    printf("\na = %i",a);

    c = a & b;

    printf("\nc = %i",c);

    if(c)
        printf("\nthe bit %i is set",M);
    else
        printf("\nthe bit %i is not set",M);
}

我之前尝试过使用添加空间%u,也尝试过fflush(stdin)但没有任何效果。

编辑:我知道不推荐使用 goto 但我必须这样做。(使用循环不是一种选择)。M 是我在编译时使用 gcc 命令行定义的宏。

4

2 回答 2

10

注意: fflush(stdin);可能是未定义的行为。阅读:为什么fflush(stdin)错了?

int fflush(FILE *ostream);
ostream指向未输入最近操作的输出流或更新流,该 函数fflush导致该流的任何未写入数据被传递到主机环境以写入文件;否则,behavior就是Undefined

您可以尝试循环并阅读直到EOF\n给出此常见问题解答条目,而不是fflush(stdin)按照我在下面的回答中建议的那样。

编辑:感谢@Jonathan Leffler

有些平台fflush(stdin)是完全定义的(作为该平台上的非标准扩展)。主要的例子是一个众所周知的系统家族,统称为 Windows。Microsoft 的规范If is open for input, clear the contents of the bufferint fflush( FILE *stream ); streamfflush

我对您的代码还有疑问;什么是M表达unsigned int b = pow(2,M-1);?如果你不定义它应该是一个错误。你发布完整的代码吗?

关于错误检测的逻辑:

如果用户输入无效,则重新读取该值

不,scanf()不返回错误代码。它返回成功转换的次数。

int scanf ( const char * 格式, ... );
返回值
成功时,函数返回参数列表中成功填充的项目数。由于匹配失败、读取错误或到达文件结尾,此计数可以与预期的项目数匹配或更少(甚至为零)。

如果发生读取错误或读取时到达文件结尾,则设置正确的指示符(feofferror)。并且,如果在任何数据可以成功读取之前发生任何一种情况,EOF则返回。

如果在解释宽字符时发生编码错误,则函数设置errnoEILSEQ.

所以实际上取决于遇到的错误,返回值可能为零,EOF。您应该使用int ferror ( FILE * stream );errno宏进行错误检测(检查链接中给出的示例)。

由于无效输入而可能出现的错误可能是:

EILSEQ: 输入字节序列没有形成有效字符。
EINVAL: 没有足够的论据;或格式为 NULL。
ERANGE: 整数转换将超过相应整数类型可存储的大小。

查看scanf 手册以获取完整列表。

无限循环的原因:

系统会跟踪到目前为止已看到哪些输入。每次调用scanf都从最后一个停止匹配输入的地方开始。这意味着如果前一个发生错误scanf,它未能匹配的输入仍然未读,就好像用户提前输入了一样。如果不注意丢弃错误输入,并且使用循环来读取输入,您的程序可能会陷入无限循环。

因此,例如在您的代码中:

x = scanf("%u", &a);
      //   ^
      //  need a number to be input

但是假设您没有输入数字,而是输入了无效的字符串,例如"name"(而不是数字,如您所说)。这将导致scanf()函数在尝试匹配无符号整数 ( "%u") 时失败,并且该单词"name"未被读取。所以下一次循环时,scanf()不等待新的用户输入,它会尝试再次转换“名称”。

类似地,如果输入是29.67,“ %u”将仅匹配前两个字符(the 29),将.67作为未读输入留给下一次调用scanf().

即使输入正确,如29,结束输入的换行符仍然未读。通常这不是问题,因为大多数转换会自动跳过前导空格,例如前一行的尾随换行符。但是,某些转换 ("%c""%[") 不会跳过任何前导空格,因此您必须手动进行。

为了避免这种无限循环一个建议:

(记住:正如我推荐的那样ferror(),错误值更适合检测无效输入。此外,它只是为了学习目的,如果你需要实现一个严肃的应用程序,你应该使用fgets(str)而不是scanf()后面跟着解析str输入来验证输入是否已验证)

input:
    //fflush(stdout); //use if needed, as \n used in printf no need of fflush-stdout
    printf("\nEnter any integer: ");
    x = scanf("%u", &a); // always wait for new symbols
    printf("%u", a);

    if(x == 0){ // x=0, if error occurred
        // read all unread chars 
        while ((ch = getchar()) != '\n' && ch != EOF);
        goto input;
    }

这只是一个可能适用于您的代码的建议(对我有用,您的代码 + gcc)。但是如果你错误地使用了这种技术,它可能会在你的代码中留下一个错误:

阅读如何刷新输入缓冲区?

如果您确定输入流中有不需要的数据,您可以使用以下一些代码片段来删除它们。但是,如果您在输入流中没有数据时调用这些,程序将等待直到有数据,这会给您带来不希望的结果。

于 2013-07-24T07:22:22.797 回答
1

这是避免无限循环的解决方法。请使用 do while 循环代替 goto:

do {
     printf("\nEnter any integer: ");
     x = scanf("%u",&a);

     printf("%u",a);
     if( x == 0)
     {
          char c;
          printf("hit any key \n");
          c = getchar();
     }

   } while(x==0);
于 2013-07-24T07:27:32.273 回答