Scanf不会第二次执行

时间:2013-07-24 07:20:58

标签: c scanf goto

我正在尝试: - 如果用户输入无效值,则重新读取该值。但问题是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命令行定义的宏。

2 个答案:

答案 0 :(得分:10)

警告:fflush(stdin);可能是未定义的行为。阅读:Why fflush(stdin) is wrong?

  

<强> int fflush(FILE *ostream);
  ostream指向输出流或更新流   最近的操作没有输入,fflush函数导致任何操作   要传递到主机环境的该流的未写入数据   写入文件;否则,behaviorUndefined

您可以尝试循环并阅读EOF\n给定this FAQ entry而不是fflush(stdin),如我在下面的回答中所建议的那样。

编辑:感谢@ Jonathan Leffler

  

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

我的代码还有其他疑问;表达式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 manual以获取完整列表。

无限循环的原因:

系统会跟踪到目前为止看到的输入。每次对scanf的调用都会从最后一个停止匹配输入的位置开始。这意味着如果前一个scanf发生错误,则无法匹配的输入仍未读取,就像用户提前输入一样。如果不小心丢弃错误输入,并且使用循环来读取输入,则程序可能会陷入无限循环。

例如,在您的代码中:

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

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

同样,如果输入为29.67,则“%u”将仅匹配前两个字符(29),将.67作为未读输入下次致电scanf()

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

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

(记住:正如我建议使用ferror(),错误值更适合检测无效输入。此外,它仅用于学习目的,如果您需要实现严肃的应用程序,则应使用{{1而不是fgets(str)后跟那个解析scanf()输入来验证输入是否有效)

str

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

阅读How do I flush the input buffer?

  

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

答案 1 :(得分: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);