Scanf不会第二次执行

sid*_*d-m 5 c goto scanf

我正在尝试: - 如果用户输入无效值,则重新读取该值.但问题只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);
}
Run Code Online (Sandbox Code Playgroud)

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

编辑:我知道不建议使用goto,但我必须这样做.(使用循环不是一个选项).M是我在编译时使用gcc命令行定义的宏.

Gri*_*han 10

警告: fflush(stdin);可能是未定义的行为.阅读:为什么fflush(stdin)错了?

int fflush(FILE *ostream);
所述ostream输出流或其中的最近操作不是输入的更新流分,fflush函数会导致该流被输送到将被写入到该文件的主机环境中的任何未写入数据; 否则,behaviorUndefined.

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

编辑:感谢@ Jonathan Leffler:

有些平台fflush(stdin)是完全定义的(作为该平台上的非标准扩展).主要的例子是一个众所周知的系统系统,统称为Windows.Microsoft的规范如果打开输入,则清除缓冲区的内容.int fflush( FILE *stream ); streamfflush

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

关于错误检测的逻辑:

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

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

int scanf(const char*format,...);
返回值
成功时,该函数返回成功填充的参数列表的项数.由于匹配失败,读取错误或文件结束的范围,此计数可以匹配预期的项目数或更少(甚至为零).

如果在读取时发生读取错误或达到文件结尾,则设置(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
Run Code Online (Sandbox Code Playgroud)

但是假设您没有输入数字,但输入了无效的字符串,例如"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;
    }
Run Code Online (Sandbox Code Playgroud)

这只是一个可能与你的代码一起使用的建议(为我工作,你的代码+ gcc).但如果您使用此技术不正确,可能会在您的代码中留下错误:

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

如果您确定输入流中包含不需要的数据,则可以使用以下某些代码段来删除它们.但是,如果在输入流中没有数据时调用它们,程序将等到存在,这会给您带来不良结果.

  • 抱怨:这太强了!有些平台完全定义了`fflush(stdin)`(作为该平台上的非标准扩展).主要的例子是一个众所周知的系统系统,统称为Windows.请参阅Microsoft的[`fflush()`]规范(http://msdn.microsoft.com/en-us/library/9yky46tz.aspx):**如果流输入打开,`fflush`清除内容缓冲区.** (2认同)