【问题标题】:While loop is stuck in infinite loop and I don't know why虽然循环卡在无限循环中,我不知道为什么
【发布时间】:2015-12-21 02:46:18
【问题描述】:
#include    <stdio.h>
#include    <stdlib.h>
#include    <ctype.h>
#include    <math.h>

int main(int argc, char * argv[])
{
   printf("This program tests your integer arithmetic skills.\n"
          "You should answer the questions following the same \n"
          "rules that computers do for integers arithmetic, not \n"
          "floating-point arithmetic. Hit the 'Enter' key after \n"
          "you have typed in your input. When you wish to finish \n"
          "the test, enter -9876 as the answer to a question.\n"
          "\n");

   int n1, n2, answer, user_answer, a, b, int_per;
   char op, c;
   float per, count, count_r, count_w;

   count = 0;
   count_r = 0;
   count_w = 0;

   printf("What is your question? ");
   scanf("%d %c %d", &n1, &op, &n2);


   do
   {
      count++;

      printf("What is %d %c %d ? ", n1, op, n2);

      if (op == '+')
      {
         answer = n1 + n2;
      }
      else if (op == '-')
      {
         answer = n1 - n2;
      }
      else if (op == '%')
      {
         answer = n1 % n2;
      }
      else if (op == '/')
      {
         answer = n1 / n2;
      }
      else if (op == '*')
      {
         answer = n1 * n2;
      }

      c = scanf("%d", &user_answer);

      if (user_answer == answer)
      {
         printf("Correct!\n\n");
         count_r++;

      }
      else if (user_answer == -9876)
      {
         count = count - 1;
         break;  
      }
      else if (c != 1)
      {
         printf("Invalid input, it must be just a number\n\n");
         printf("What is %d %c %d ? ", n1, op, n2);
      }
      else if (user_answer != answer)
      {
         printf("Wrong!\n\n");
         count_w++;
      }

   } while(user_answer != -9876);

   per = (count_r / count) * 100;

   a = (int) count_r;
   b = (int) count_w;
   int_per = roundf(per);

   printf("\nYou got %d right and %d wrong, for a score of %d%c\n", a,
          b, int_per, 37);

   return EXIT_SUCCESS;

}

上面的代码应该循环询问问题和答案,直到用户输入 -9876 作为答案,然后程序终止并给他们分数。这一切都有效,除了!!一方面。当用户在输入中输入非数字时。发生这种情况时,它应该说“输入无效,请重试”,然后再次询问相同的问题。例如

你的问题是什么? 9+9

什么是 9 + 9? 嗯,8

输入错误,请重试

什么是 9 + 9?

SO.. 用户输入了“hmmm”,而不是再次提示用户相同的问题,然后正确扫描,它只是跳入了一个无限循环。我想知道如何解决这个问题。

谢谢

【问题讨论】:

  • 输入无效时清除输入缓冲区。
  • 当您使用scanf("%d", &amp;value) 扫描整数并且输入不是有效整数时,scanf 返回 0 并且输入返回到它开始的位置,即在非数字输入之前。随后的调用将尝试一遍又一遍地重新解析相同的无效输入。你应该scanf("%*s") 跳过它。更好的是,将带有fgetssscanf 的整行读入其中。
  • 我不明白为什么要使用输入-9876
  • 您在 scanf 函数中使用了 %d %c %d 之间的空格..将垃圾值分配到 &n1、&op、&n2 变量中
  • @NeErAjKuMaR:不,它没有;格式字符串中的空格只是告诉scanf 跳过空白字符。

标签: c while-loop infinite-loop


【解决方案1】:

通话中

c = scanf("%d", &user_answer);

%d 转换说明符期望看到十进制数字字符序列;它将告诉scanf 跳过任何前导空格,然后读取十进制数字字符直到第一个非十进制数字字符,然后将结果转换并保存到user_answer。如果您输入 12aEnterscanf 将读取并使用 '1' 和 @ 987654327@ 个字符,将值 12 分配给 user_answer 并返回 1(对于一次成功的转换和分配),在输入流中留下 'a' 和换行符。

当您键入"hmmm" 时,第一个非空白字符不是十进制数字,因此scanf 将其保留在原位,不为user_answer 分配任何内容,并返回0。使用"%d" 转换说明符对scanf 的所有剩余调用都将执行相同的操作。

因此,您需要确保您的 scanf 成功,如果没有,请在再次读取之前清除输入流中的所有字符,如下所示:

if ( (c = scanf( "%d", &user_answer ) ) == 0 )
{
  /**
   * input matching failure, clear stray characters from input stream
   * up to the next newline character
   */
  while ( getchar() != '\n' )
    ; // empty loop 
}
else if ( c == EOF )
{
  // error occurred during input operation
}
else
{
  // do something with user_answer
}

您会注意到,在我的第一个示例中,%d 转换说明符接受了输入 "12a";它将12 转换并分配给user_answer,将'a' 字符留在输入流中以破坏下一次读取。理想情况下,您希望完全拒绝此类格式错误的输入。您可以执行以下操作:

/**
 * Repeatedly prompt and read input until we get a valid decimal string
 */
for( ;; ) 
{
  int c, dummy;
  printf("What is %d %c %d ? ", n1, op, n2);

  if ( ( c = scanf("%d%c", &user_answer, &dummy ) == 2 )
  {
    /**
     * If the character immediately following our numeric input is
     * whitespace, then we have a good input, break out of the read loop
     */
    if ( isspace( dummy ) )
      break;
    else
    {
      fprintf( stderr, "Non-numeric character follows input, try again...\n" );
      while ( getchar() != '\n' )
        ; // empty loop body
    }
  }
  else if ( c == 1 )
  {
    /**
     * No character following successful decimal input, meaning we
     * hit an EOF condition before any trailing characters were seen.  
     * We'll consider this a good input for our purposes and break
     * out of the read loop.
     */
    break;
  }
  else if ( c == 0 )
  {
    /**
     * User typed in one or more non-digit characters; reject the input
     * and clear out the input stream
     */
    fprintf( stderr, "Non-numeric input\n" );

    /**
     * Consume characters from the input stream until we see a newline.
     */
    while ( ( getchar() != '\n' )
      ; // empty loop body
  }
  else
  {
    /**
     * Input error or EOF on read; we'll treat this as a fatal
     * error and bail out completely.
     */
    fprintf( stderr, "Error occurred during read, panicking...\n" );
    exit( 0 );
  }
}

另一种选择是将您的输入读取为文本,然后使用strtol 库函数将其转换为结果类型:

for ( ;; )
{
  char input[SIZE]; // for some reasonable SIZE value, at least 12 to handle
                    // a 32-bit int (up to 10 digits plus sign plus
                    // string terminator

  printf("What is %d %c %d ? ", n1, op, n2);
  if ( fgets( input, sizeof input, stdin ) )
  {
    char *check; // will point to first non-digit character in input buffer
    int tmp = (int) strtol( input, &check, 10 );
    if ( isspace( *check ) || *check == 0 )
    {
      user_answer = tmp;
      break;
    }
    else
    {
      fprintf( stderr, "%s is not a valid input, try again\n", input );
    }
  }
  else
  {
    /** 
     * error or EOF on input, treat this as a fatal error and bail
     */
    fprintf( stderr, "EOF or error while reading input, exiting...\n" );
    exit( 0 );
  }
}

这是我的首选方法。

这些混乱中的任何一个都会取代该行

c = scanf("%d", &user_answer);

在阅读完您可能在想的所有内容后,“C 中的交互式输入确实是一件令人头疼的事情”。你是对的。

【讨论】:

    【解决方案2】:
    scanf("%d%c%d",&n1, &op, &n2) use this code 
    

    【讨论】:

    • 这并没有解决OP的问题,并引入了一个新问题。第一个%d%c 转换说明符之间至少需要有一个空格,否则op 将在第一个输入之后立即拾取第一个字符,无论是运算符还是空格。
    【解决方案3】:

    当读取输入出现错误时,您应该清除错误状态并跳过该行的其余部分,然后再尝试读取更多用户输入。

    替换行:

      else if (c != 1)
      {
         printf("Invalid input, it must be just a number\n\n");
         printf("What is %d %c %d ? ", n1, op, n2);
      }
    

    通过

      else if (c != 1)
      {
         printf("Invalid input, it must be just a number\n\n");
    
         // Clear the error states.
         clearerr(stdin);
    
         // Skip the rest of the line
         skipRestOfLine(stdin);
    
         printf("What is %d %c %d ? ", n1, op, n2);
      }
    

    skipRestOfLine() 可以实现为:

    void skipRestOfLine(FILE* stream)
    {
       int c;
       while ( (c = fgetc(stream)) != '\n' && c != EOF );
    }
    

    【讨论】:

    • 为什么是fgetc() 而不是getc()
    • @JonathanLeffler,我不明白为什么一个会比另一个更好。
    • getc() 可以是宏; fgetc() 保证是一个函数。如果getc() 是一个宏,那是因为它可以执行得更好。在某种程度上,这可以追溯到内联函数之前的日子。这不是什么大问题,但通常认为使用getchar()getc() 比使用fgetc() 更好。
    • @JonathanLeffler,感谢您的解释。今天学到了一些东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 2020-07-13
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多