fflush 是如何工作的?

How does fflush work?

我不确定我是否正确理解了 C 中刷新的工作原理。我只是无法按照多本手册和参考书中的描述让它工作。这是一个带有注释的示例:

#include <stdio.h>


int main(void) {
    int  x;
    char ch;

    printf("Prompt: ");
    scanf("%d", &x); /* I key in 67 and press Enter. At this very moment,
                        the input buffer should contain the ASCII codes
                        for the numbers 6 and 7 as well as the ASCII code
                        for the newline character, which is 10. The scanf
                        function is going to find the ASCII codes for 6
                        and 7, convert them to the integer 67, assign it
                        to the variable x and remove them from the
                        buffer. At this point, what still remains in the
                        buffer is the newline character. */
    fflush(stdin);   /* I want to do some more input. So, I flush the
                        buffer to remove anything that still might be
                        there, but it doesn't really look like fflush is
                        doing that. */

    printf("Prompt: "); /* I'm not going to be able to get my hands on
                           the following line of code because fflush is
                           not doing its job properly. The remaining
                           newline character is going to be read into the
                           variable ch automatically, thus not allowing
                           me to enter anything from the keyboard. */
    scanf("%c", &ch);

    printf("x: %d, ch: %d\n", x, ch);
    /*
       OUTPUT:
       Prompt: 67
       Prompt: x: 67, ch: 10
    */


    return 0;
}

不要执行 fflush(stdin);,它会调用 undefined behavior

引用 C11,

Ifstream points to an output stream or an update stream in which the most recent operation was not input, the fflush function causes any unwritten data for that stream to be delivered to the host environment to be written to the file; otherwise, the behavior is undefined.

并且stdin不是输出流。

为了完成:

一些实现确实在输入流上定义了 fflush。例如 Microsoft's msvcrt and GNU libc. Others, like the BSD libc, may additionally provide a separate function 用于清除缓冲区。

这些函数除了不可移植外,还有一些严重的缺点:它们通常在假设缓冲区看起来有某种特定方式的情况下使用,例如有一个换行符。

stdin 可能连接到一个文件,它的缓冲区可能包含的不仅仅是换行符。如果清除输入流,将跳过这些缓冲数据。

此外,在 libc 的 stdio 之外可以有不受影响的缓冲区。

因此,您应该明确读取并丢弃不需要的数据。

除了 fflush() 没有像 Sourav 指出的那样在输入流上定义...

I key in 67 and press Enter. At this very moment, the input buffer should contain the ASCII codes for the numbers 6 and 7 as well as the ASCII code for the newline character, which is 10. The scanf function is going to find the ASCII codes for 6 and 7, convert them to the integer 67, assign it to the variable x and remove them from the buffer. At this point, what still remains in the buffer is the newline character.

如果用户确实输入了非数字的内容,则可能会发生其他情况。

如果在第一个数字之前有非数字,x将不会被初始化。你不会知道,因为你没有检查 return 值 共 scanf().

用户可能已经预料到下一个提示(对于一个字符),并输入了类似 67 x 的内容,期望 67 满足您的第一个提示,而 x你的第二个。 (他不会高兴你的程序删除了他的部分条目。)

在输入中使用 *scanf() 你不能 确定 是预期的格式(用户输入,而不是例如回读你自己用 [=18 写的东西=]) 很脆弱。

所以我的一般建议是 不要对用户输入使用 *scanf() 函数,而是使用 fgets() 逐行读取用户输入,然后解析输入-闲暇时记忆。

你有许多更强大的功能供你使用,你可以更细粒度地处理错误情况(包括能够给出在任何输入的 full 行错误消息)。

以下只是粗略的示意图;根据您的应用程序,您需要以不同方式组织:

const size_t BUFFERSIZE = 1024;
char buffer[ BUFFERSIZE ];
long x;
char ch;
printf( "Prompt: " );
if ( fgets( buffer, BUFFERSIZE, stdin ) == NULL )
{
    // Error occurred, use feof() / ferror() as appropriate
}
else
{
    size_t len = strlen( buffer );
    if ( buffer[ len - 1 ] != '\n' )
    {
        // The line entered was too long for the buffer,
        // there is unread input. Handle accordingly, e.g.
        // resizing the buffer and reading the rest.
    }
    else
    {
        // You got the whole line; blot out the newline
        buffer[ len - 1 ] = '[=10=]';

        // Assuming a decimal number first
        char * curr = buffer;
        errno = 0;
        long x = strtol( curr, &curr, 10 );
        if ( errno == ERANGE )
        {
            // Number exceeds "long" range, handle condition
        }
        else if ( curr == buffer )
        {
            // What you got was not a number, handle condition
        }

        // Keep parsing until you hit end-of-string
    }
}