为什么我必须连续两次使用 gets() 函数?

Why do I have to use gets() function twice in a row?

我对下面的代码有一个不寻常的问题。

void Menu()
{
    bool end = 0;
    int n;
    while (!end)
    {
        scanf("%d", &n);
        switch(n)
        {
            case 1:
                my_strlen_show();
                break;
            //other irrelevant cases 2-6
            case 7:
                end = 1;
                break;
            default:
                printf("ERROR");
                break;
        }
    }       
}

int my_strlen(const char *str)
{
    int count = 0;
    for (; *str != '[=10=]' ; str++)
    {
        count++;
    }
    return count;
}

void my_strlen_show()
{
    
    char tab[1000];
    printf("\n\nEnter a sentence: ");
    gets(tab);
    gets(tab);
    printf("\nWritten sentence has %d characters.\n\n", my_strlen(tab));
    return;
}

我不知道为什么我必须写两次 gets(tab) 才能使程序正常运行。当我使用一次时,my_strlren_show() 函数立即执行并显示该句子有 0 个字符。我知道我可以在 for 循环中使用其他方法,例如 scanf() 函数,但我很好奇为什么这种方法适用于一种奇特的方式。

谁能解释为什么会这样?我将不胜感激。

您的第一个 scanf 仅从 stdin 中读取一个整数。当您在输入整数后按回车键时,一个换行符 (\n) 将发送到 stdin,它就停留在那里 - 等待被从 stdin 读取的下一个函数拾取。

下一个 gets 然后读取这个换行符并立即 returns。所以你需要另一个 gets 来实际读取输入。

话虽如此,您甚至不应该首先使用 gets - 这是一个已弃用的函数。最重要的是,考虑将 fgets 用于 读取输入 scanf其实是输入解析函数,不是读取函数。它只读取它可以解析的内容,并将其他所有内容留在 stdin.

如果您仍然决定走 scanf 路线,您应该使用 "%d\n" 在第一个输入中使用换行符 - 不仅如此,您 必须 还检查 scanf 的 return 值,它 return 是它能够解析的值的数量。

现在下一个 fgets 调用将不必使用剩余的换行符。它将等待另一行用户输入(注意换行符将包含在 fgets 读入的缓冲区中)-

char tab[1000];
printf("\n\nEnter a sentence: ");
if (fgets(tab, 1000, stdin) == NULL)
{
    // handle error during reading
    return;
}
// tab now has the user input, delimited with a `\n`
// if you want to get rid of this newline - use `strcspn`
tab[strcspn(name, "\n")] = 0

strcspn

上的文档

不过,我建议您使用完整的 fgets 路线并使用 sscanf 进行整数解析。

int n;
char buff[4096];
if (fgets(buff, 4096, stdin) == NULL)
{
    // handle error during reading
    return;
}
if (sscanf(tab, "%d", &n) != 1)
{
    // parsing failed - sscanf should've parsed exactly 1 value
    // handle error
    return;
}
// use n here

这里有一份关于 how to move away from scanf 的完整指南 - 其中会提到这个具体问题。

不要使用gets()。它危险的不安全性使它获得了属于一小部分函数的可疑区别,这些函数已从 C 语言标准中撤回

但是,如果您更改为 fgets:

,您可能会遇到同样的问题
    fgets(tab, sizeof(tab), stdin);

问题是 gets()fgets() 读完了当前行的末尾(或者在 fgets() 的情况下直到缓冲区被填满)。前面的 scanf() 只消耗了十进制整数末尾的字节,将该行的其余部分留在输入流中,等待读取。这至少包括一个标记行尾的换行符。在使用 fgets()gets() 读取想要的输入之前,必须消耗掉它。实现这一目标的一种方法是:

if ((scanf("%*[^\n]") == EOF) || (getchar() == EOF)) {
    // handle end-of-file or I/O error ...
}

scanf 读取并丢弃下一个换行符之前的任何字符,并且假设未到达文件末尾并且没有发生 I/O 错误,getchar() 消耗换行符本身。