为什么scanf()会在此代码中导致无限循环?

时间:2009-11-11 15:39:30

标签: c gcc scanf

我有一个小的C程序,它只是从stdin读取数字,每个循环周期一个。如果用户输入了一些NaN,则应该向控制台输出错误,输入提示应该再次返回。输入“0”时,循环应该结束,给定的正/负值的数量应该打印到控制台。这是该计划:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

我的问题是,在输入一些非数字(如“a”)时,这会导致无限循环一遍又一遍地写“ - &gt; Err ...”。我想这是一个scanf()问题,我知道这个函数可以被更安全的替换,但是这个例子适用于初学者,只知道printf / scanf,if-else和循环。

我已经阅读了this question的答案并浏览了其他问题,但没有真正回答这个具体问题。

16 个答案:

答案 0 :(得分:36)

scanf仅使用与格式字符串匹配的输入,返回消耗的字符数。任何与格式字符串不匹配的字符都会导致它停止扫描并使无效字符仍在缓冲区中。正如其他人所说,在继续之前,仍然需要从缓冲区中清除无效字符。这是一个非常脏的修复,但它将从输出中删除有问题的字符。

char c = '0';
if (scanf("%d", &number) == 0) {
  printf("Err. . .\n");
  do {
    c = getchar();
  }
  while (!isdigit(c));
  ungetc(c, stdin);
  //consume non-numeric chars from buffer
}

编辑修复了代码,一次性删除了所有非数字字符。不再为每个非数字字符打印多个“Errs”。

Here是一个非常好的scanf概述。

答案 1 :(得分:7)

我认为你必须在继续循环之前刷新缓冲区。这样的事情可能会起作用,虽然我无法测试我在这里写的东西:

int c;
while((c = getchar()) != '\n' && c != EOF);

答案 2 :(得分:6)

scanf()将“a”留在输入缓冲区中以供下次使用。您应该使用getline()来读取一行,无论如何,然后使用strtol()或类似内容进行解析。

(是的,getline()是特定于GNU的,而不是POSIX。那又怎么样?问题是标记为“gcc”和“linux”。getline()也是读取一行的唯一合理选项除非您想手动完成所有文本。)

答案 3 :(得分:3)

由于其他答案指出scanf存在问题,您应该考虑使用其他方法。对于任何严肃的输入读取和处理,我总是发现scanf方式太有限了。最好只用fgets读取整行,然后用strtokstrtol等函数处理它们(BTW会正确地解析整数并告诉你无效字符的确切位置)开始)。

答案 4 :(得分:3)

使用scanf()fgets(),而不是使用sscanf()并且必须处理具有无效字符的缓冲区。

/* ... */
    printf("0 to quit -> ");
    fflush(stdout);
    while (fgets(buf, sizeof buf, stdin)) {
      if (sscanf(buf, "%d", &number) != 1) {
        fprintf(stderr, "Err...\n");
      } else {
        work(number);
      }
      printf("0 to quit -> ");
      fflush(stdout);
    }
/* ... */

答案 5 :(得分:3)

我有类似的问题。我只使用scanf解决了。

Input "abc123<Enter>"了解它是如何运作的。

#include <stdio.h>
int n, num_ok;
char c;
main() {
    while (1) {
        printf("Input Number: ");
        num_ok = scanf("%d", &n);
        if (num_ok != 1) {
            scanf("%c", &c);
            printf("That wasn't a number: %c\n", c);
        } else {
            printf("The number is: %d\n", n);
        }
    }
}

答案 6 :(得分:3)

在某些平台(尤其是Windows和Linux)上,您可以使用fflush(stdin);

#include <stdio.h>

int main(void)
{
  int number, p = 0, n = 0;

  while (1) {
    printf("-> ");
    if (scanf("%d", &number) == 0) {
        fflush(stdin);
        printf("Err...\n");
        continue;
    }
    fflush(stdin);
    if (number > 0) p++;
    else if (number < 0) n++;
    else break; /* 0 given */
  }

  printf("Read %d positive and %d negative numbers\n", p, n);
  return 0;
}

答案 7 :(得分:1)

解决方案::从fflush(stdin);返回0时,您需要添加scanf

原因:遇到错误时,它似乎将输入字符保留在缓冲区中,因此每次调用scanf时,它只会继续尝试处理无效字符,但是永远不要将其从缓冲区中删除。当您调用fflush时,将清除输入缓冲区(stdin),因此将不再重复处理无效字符。

您修改过的程序:以下是您的程序经过了必要的更改。

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            fflush(stdin);
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

答案 8 :(得分:1)

.what-i-do {
  font-family: "Lato", sans-serif;
  color: white;
  line-height: 1.8;
  letter-spacing: 0.15em;
  text-align: center;
  justify-content: center;
  font-size: 18px;
  font-weight: 700;
}

.connect-title {
  font-family: "Lato", sans-serif;
  color: white;
  line-height: 1.8;
  letter-spacing: 0.15em;
  text-align: center;
  justify-content: center;
  font-size: 18px;
  font-weight: 700;
}

答案 9 :(得分:0)

我有同样的problem,我找到了一个有点hacky的解决方案。我使用fgets()来阅读输入,然后将其输入sscanf()。这对于无限循环问题来说并不是一个糟糕的修复,并且通过一个简单的for循环,我告诉C搜索任何无数字字符。以下代码不允许输入123abc

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

int main(int argc, const char * argv[]) {

    char line[10];
    int loop, arrayLength, number, nan;
    arrayLength = sizeof(line) / sizeof(char);
    do {
        nan = 0;
        printf("Please enter a number:\n");
        fgets(line, arrayLength, stdin);
        for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array
            if(line[loop] == '\n') { // stop the search if there is a carrage return
                break;
            }
            if((line[0] == '-' || line[0] == '+') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers
                continue;
            }
            if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop
                nan++;
                break;
            }
        }
    } while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter
    sscanf(line, "%d", &number);
    printf("You enterd number %d\n", number);
    return 0;
}

答案 10 :(得分:0)

尝试使用:

{{1}}

这对我来说很好......试试这个.. 继续语句不合适,因为 Err .. 应该只执行一次。所以,试试打破我测试过......这对你来说很好......我测试过....

答案 11 :(得分:0)

当输入非数字时,会发生错误,并且非数字仍保留在输入缓冲区中。你应该跳过它。即使是像1a这样的符号组合也会首先读作数字1我认为你也应该跳过这样的输入。

该程序可以通过以下方式查看。

#include <stdio.h>
#include <ctype.h>

int main(void) 
{
    int p = 0, n = 0;

    while (1)
    {
        char c;
        int number;
        int success;

        printf("-> ");

        success = scanf("%d%c", &number, &c);

        if ( success != EOF )
        {
            success = success == 2 && isspace( ( unsigned char )c );
        }

        if ( ( success == EOF ) || ( success && number == 0 ) ) break;

        if ( !success )
        {
            scanf("%*[^ \t\n]");
            clearerr(stdin);
        }
        else if ( number > 0 )
        {
            ++p;
        }
        else if ( number < n )
        {
            ++n;
        }
    }

    printf( "\nRead %d positive and %d negative numbers\n", p, n );

    return 0;
}

程序输出可能看起来像

-> 1
-> -1
-> 2
-> -2
-> 0a
-> -0a
-> a0
-> -a0
-> 3
-> -3
-> 0

Read 3 positive and 3 negative numbers

答案 12 :(得分:0)

要部分解决您的问题,我只需在scanf之后添加以下行:

fgetc(stdin); /* to delete '\n' character */

下面,您的代码行如下:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            fgetc(stdin); /* to delete '\n' character */
            printf("Err...\n");
            continue;
        }

        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

但是,如果您输入多个字符,则程序将一个字符一个字符地继续直到“ \ n”。

所以我在这里找到了解决方案:How to limit input length with scanf

您可以使用以下行:

int c;
while ((c = fgetc(stdin)) != '\n' && c != EOF);

答案 13 :(得分:-1)

扫描前刷新输入缓冲区:

while(getchar() != EOF) continue;
if (scanf("%d", &number) == 0) {
    ...

我打算建议fflush(stdin),但显然会产生undefined behavior

在回复您的评论时,如果您希望显示提示,则必须刷新输出缓冲区。默认情况下,只有在打印换行符时才会发生这种情况。像:

while (1) {
    printf("-> ");
    fflush(stdout);
    while(getchar() != EOF) continue;
    if (scanf("%d", &number) == 0) {
    ...

答案 14 :(得分:-1)

嗨,我知道这是一个旧帖子,但我刚刚完成了一个学校作业,我遇到了同样的问题。 我的解决方案是我使用gets()来获取留下的scanf()。

这是稍微重写的OP代码;对他来说可能毫无用处,但也许它会帮助其他人。

#include <stdio.h>

    int main()
    {
        int number, p = 0, n = 0;
        char unwantedCharacters[40];  //created array to catch unwanted input
        unwantedCharacters[0] = 0;    //initialzed first byte of array to zero

        while (1)
        {
            printf("-> ");
            scanf("%d", &number);
            gets(unwantedCharacters);        //collect what scanf() wouldn't from the input stream
            if (unwantedCharacters[0] == 0)  //if unwantedCharacters array is empty (the user's input is valid)
            {
                if (number > 0) p++;
                else if (number < 0) n++;
                else break; /* 0 given */
            }
            else
                printf("Err...\n");
        }
        printf("Read %d positive and %d negative numbers\n", p, n);
        return 0;
    }

答案 15 :(得分:-1)

晚上好。我最近遇到了同样的问题,我找到了一个可能对很多人有帮助的解决方案。好吧,实际上函数“scanf”会在内存中留下缓冲区......这就是导致无限循环的原因。因此,如果初始scanf包含“null”值,则实际上必须将此缓冲区“存储”到另一个变量。这就是我的意思:

#include <stdio.h>
int n;
char c[5];
main() {
    while (1) {
        printf("Input Number: ");
        if (scanf("%d", &n)==0) {  //if you type char scanf gets null value
            scanf("%s", &c);      //the abovementioned char stored in 'c'
            printf("That wasn't a number: %s\n", c);
        }
        else printf("The number is: %d\n", n);
    }
}