【问题标题】:C Program infinite loop unexpectedly when used scanf使用scanf时C程序意外无限循环
【发布时间】:2014-08-14 06:11:05
【问题描述】:

我到处检查,浪费了大约 3 个小时来寻找解决方案。我的程序本身就是无限循环。这是我的 C 程序:

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

int main (void)

{
    int attempts = 0;
    char password[10];

    do
    {
        printf("Enter your password:\n");
        scanf("%[^\n]s", password);
        printf("\n");
        attempts++;
    } while (strcmp(password, "awesome 123 ok"));

    printf("You entered a correct password in %d attempts!", attempts);

    return 0;
}

我试过scanf("%[A-Za-z0-9 ]s", password)"所以它可以将包括空格在内的所有字符和数字作为输入,但它只是循环。而且我还尝试使用getchar(),但即使我输入正确的密码,它也会一次又一次地要求输入密码。任何帮助将不胜感激。

【问题讨论】:

  • 您不需要"%[^\n]s" 格式的s。只使用"%[^\n]"
  • 强制:检查所有scanf函数的返回值,始终。
  • 检查一下你的 scanf 语法。可能你也可以使用gets(password)或者更正scanf("%[^\n]", password);
  • @Avinash 不不不,永远不要建议任何人使用gets。它容易出现缓冲区溢出,在 C 标准中已被弃用很长时间,甚至在最新的 C11 标准中被删除。
  • @Avinash 永远不要使用gets!它已损坏,已弃用,最终在 C11 中删除。例如使用fgets 或非标准getline

标签: c scanf infinite-loop


【解决方案1】:

awesome 123 ok 的大小为 15,包括 \0。但是您正在为 10 个字节分配内存。它会导致未定义的行为。

当您使用%[^\n] 格式说明符时,无需使用s,它也会自动扫描空格。

尝试以下更改-

int main (void)

{
    int attempts = 0;
    char password[20]; // Fix 1

    do
    {
        printf("Enter your password:\n");
        scanf("%[^\n]", password); // Fix 2
        printf("\n");
        attempts++;
    } while (strcmp(password, "awesome 123 ok"));

    printf("You entered a correct password in %d attempts!", attempts);

    return 0;
}

【讨论】:

  • 2个问题:1)如果用户输入"\n"password里面会有随机数据。 2) 在第二个循环中,第一行的"\n" 保留在stdin 中。阻止任何新条目。建议fgets().
【解决方案2】:

您声明了char password[10];,但您将其与具有更多字符的awesome 123 ok 进行比较。

【讨论】:

  • 哦!这对我来说很尴尬。感谢您指出这一点:)
【解决方案3】:

来自this scanf (and family) reference

除 [、c 和 n 之外的所有转换说明符都会在尝试解析输入之前消耗并丢弃所有前导空白字符。

这意味着结尾的换行符(一个空白字符)将包含在循环中对scanf 的下一次调用中。

解决方案很简单:告诉scanf 读取并丢弃前导空格:

scanf(" %[^\n]", password);
/*     ^              */
/*     |              */
/* Note leading space */

还请注意,我删除了格式中尾随的s,因为这告诉scanf 在输入中期待文字s"%[" 格式以结束 ']' 结尾。


您可能还想限制读取的字符数,以免溢出您读入的缓冲区:

scanf(" %9[^\n]", password);

请注意,上述格式将最大字段宽度设置为 9 个字符,因为缓冲区还需要包含终止符 '\0'。如果您增加缓冲区大小,请修改此数字,但请记住,它应该(最多)比缓冲区大小小一。

【讨论】:

    【解决方案4】:

    将十进制密码[10]更改为密码[20]

    【讨论】:

      【解决方案5】:

      我会使用 fgets,因为它比 get 或 scanf 更安全:

      #include <stdio.h>
      #include <string.h>
      
      int main (void) {
          int attempts = 0;
          char password[20];
      
          do {
              printf("Enter your password:\n");
              fgets(password, sizeof(password), stdin);
              password[strlen(password) - 1] = '\0';
              printf("\n");
              attempts++;
          } while (strcmp(password, "awesome 123 ok"));
          printf("You entered a correct password in %d attempts!", attempts);
          return 0;
      }
      

      现在,只有像我在示例中那样增加密码[] 的大小时,这才有效。当不使用 fgets 时,它可能仍然会以不好的方式工作,因为您正在比较缓冲区溢出。

      【讨论】:

      • 谢谢,fgets 对我来说是新的,我今天学到了一些新东西! :)
      【解决方案6】:

      问题是password 太窄了。正因为如此,程序通常会写到数组末尾之后,导致undefined behaviour

      您可以通过扩大password 来解决此问题。但是,您的程序仍将对stack smashing 开放:攻击者可以通过输入精心设计的长密码字符串来执行任意代码。

      要解决此问题,您需要更改 scanf() 格式说明符以限制它可以在 password 中存储的字符数。

      以下更改将解决这两个问题:

          char password[32]; /* more space: 31 characters + NUL */
          do {
              ...
              scanf("%31[^\n]%*c", password); /* format specifier */
      

      后者将确保您在password 中读取的字符永远不会超过 31 个;它还使用换行符而不存储它。

      【讨论】:

      • %*c 是做什么的?和%c一样吗?
      • @Amession:是的,除了它不存储它。
      【解决方案7】:

      添加getchar() 以使用\n 字符。在下面找到修改后的代码。

      int main (void)
      
      {
          int attempts = 0;
          char password[10];
      
          do
          {
              printf("Enter your password:\n");
              scanf("%[^\n]s", password);
              getchar (); // Fix1
      
              printf("\n");
              attempts++;
          } while (strcmp(password, "awesome 123 ok"));
      
          printf("You entered a correct password in %d attempts!", attempts);
      
          return 0;
      }
      

      您的代码中未正确使用数组password。所以改变它的逻辑。用户可以输入 N 个字符。所以限制用户输入到有限的字符或使用动态内存分配

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-22
        • 2012-04-09
        • 1970-01-01
        • 2015-03-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多