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

问题描述

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

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 != '\0' ; 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() 函数,但我很好奇为什么这种方法以一种特殊的方式工作。 >

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

解决方法

不要使用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() 会消耗换行符本身。

,

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

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

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

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

现在下一个 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 的完整指南 - 将提到这个特定问题。