C使用scanf时意外编程无限循环

时间:2014-08-14 06:11:05

标签: c scanf infinite-loop

我在这里和那里检查并浪费了大约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(),但即使我输入正确的密码,它也会一次又一次地要求输入密码。任何帮助将不胜感激。

7 个答案:

答案 0 :(得分:6)

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;
}

答案 1 :(得分:3)

您声明char password[10];但是将其与包含更多字符的awesome 123 ok进行比较。

答案 2 :(得分: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'字符。如果增加缓冲区大小,请修改此数字,但请记住它应该(最多)小于缓冲区大小。

答案 3 :(得分:1)

将dec密码[10]更改为密码[20]

答案 4 :(得分:1)

问题是password太窄了。因此,程序会定期写入数组的末尾,从而产生undefined behaviour

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

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

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

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

后者将确保您永远不会将超过31个字符读入password;它也会消耗新行而不存储它。

答案 5 :(得分:1)

我会使用fgets,因为它比获取或扫描更安全:

#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时,它可能仍然以错误的方式工作,因为您正在比较缓冲区溢出。

答案 6 :(得分:0)

添加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个字符。因此,将用户输入限制为有限字符或使用动态内存分配