C语言 使用 fflush(stdin)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2979209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:36:20  来源:igfitidea点击:

Using fflush(stdin)

cstdinfflush

提问by wrongusername

So a quick Google search for fflush(stdin)for clearing the input buffer reveals numerous websites warning against using it. And yet that's exactly how my CS professor taught the class to do it.

因此,在 Google 上快速搜索以fflush(stdin)清除输入缓冲区会显示许多网站警告不要使用它。然而,这正是我的 CS 教授教全班这样做的方式。

How bad is using fflush(stdin)? Should I really abstain from using it, even though my professor is using it and it seems to work flawlessly?

使用有多糟糕fflush(stdin)?我真的应该放弃使用它,即使我的教授正在使用它并且它似乎完美地工作吗?

采纳答案by Eli Bendersky

Simple: this is undefined behavior, since fflushis meant to be called on an output stream. This is an excerpt from the C standard:

简单:这是未定义的行为,因为fflush旨在在输出流上调用。这是 C 标准的摘录:

int fflush(FILE *ostream);

ostream 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.

int fflush(FILE *ostream);

ostream 指向一个输出流或一个更新流,其中没有输入最近的操作,fflush 函数会导致该流的任何未写入的数据被传递到主机环境以写入文件;否则,行为未定义。

So it's not a question of "how bad" this is. fflush(stdin)is plainly wrong, and you mustn't use it, ever.

所以这不是“有多糟糕”的问题。显然fflush(stdin)错误的,你永远不能使用它

回答by Jonathan Leffler

Converting comments into an answer — and extending them since the issue reappears periodically.

将评论转换为答案 - 并在问题定期重新出现后对其进行扩展。

Standard C and POSIX leave fflush(stdin)as undefined behaviour

标准 C 和 POSIX 保留fflush(stdin)为未定义行为

The POSIX, C and C++ standards for fflush()explicitly state that the behaviour is undefined, but none of them prevent a system from defining it.

POSIX,C和C ++标准fflush()明确说明该行为是未定义的,但它们都没有防止系统定义它。

ISO/IEC 9899:2011 —?the C11 Standard — says:

ISO/IEC 9899:2011——C11 标准——说:

§7.21.5.2 The fflush function

?2 If streampoints to an output stream or an update stream in which the most recent operation was not input, the fflushfunction 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.

§7.21.5.2 fflush 函数

?2 如果stream指向一个输出流或一个更新流,其中没有输入最近的操作,该fflush函数会导致该流的任何未写入数据被传送到主机环境以写入文件;否则,行为未定义。

POSIX mostly defers to the C standard but it does mark this text as a C extension.

POSIX 主要遵循 C 标准,但它确实将此文本标记为 C 扩展。

[CX] For a stream open for reading, if the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream, and any characters pushed back onto the stream by ungetc()or ungetwc()that have not subsequently been read from the stream shall be discarded (without further changing the file offset).

[CX] 对于一个打开读取的流,如果文件不在EOF,并且该文件是可以查找的,则底层打开文件描述的文件偏移量应设置为该流的文件位置,任何被推回到流上的字符ungetc()ungetwc()随后没有从流中读取的字符应被丢弃(不进一步更改文件偏移量)。

Note that terminals are not capable of seeking; neither are pipes or sockets.

请注意,终端不能搜索;管道或套接字都不是。

Microsoft defines the behaviour of fflush(stdin)

微软定义的行为 fflush(stdin)

Microsoftand the Visual Studio runtime defines the define the behaviour of fflush()on an input stream.

Microsoft和 Visual Studio 运行时定义fflush()了输入流上的行为。

If the stream is open for input, fflushclears the contents of the buffer.

如果流为输入打开,则fflush清除缓冲区的内容。

M.Mnotes:

MM注意

Cygwin is an example of a fairly common platform on which fflush(stdin)does not clear the input.

Cygwin 是一个相当常见的平台的例子,它fflush(stdin)不会清除输入。

This is why this answer version of my commentnotes 'Microsoft and the Visual Studio runtime' — if you use a non-Microsoft C runtime library, the behaviour you see depends on that library.

这就是为什么我的评论的这个答案版本指出“Microsoft 和 Visual Studio 运行时”——如果您使用非 Microsoft C 运行时库,则您看到的行为取决于该库。

Linux documentation and practice seem to contradict each other

Linux 文档和实践似乎相互矛盾

Surprisingly, Linuxnominally documents the behaviour of fflush(stdin)too, and even defines it the same way (miracle of miracles).

令人惊讶的是,Linux名义上记录了fflush(stdin)too的行为,甚至以同样的方式定义了它(奇迹的奇迹)。

For input streams, fflush()discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.

对于输入流,fflush()丢弃已从底层文件中提取但尚未被应用程序使用的任何缓冲数据。

I remain a bit puzzled and surprised at the Linux documentation saying that fflush(stdin)will work. Despite that suggestion, it most usually does not work on Linux. I just checked the documentation on Ubuntu 14.04 LTS; it says what is quoted above, but empirically, it does not work —?at least when the input stream is a non-seekable device such as a terminal.

我对 Linux 文档说这fflush(stdin)会起作用仍然有点困惑和惊讶。尽管有这个建议,但它通常在 Linux 上不起作用。我刚刚查看了 Ubuntu 14.04 LTS 上的文档;它说明了上面引用的内容,但根据经验,它不起作用 - 至少当输入流是不可搜索的设备(如终端)时。

demo-fflush.c

demo-fflush.c

#include <stdio.h>

int main(void)
{
    int c;
    if ((c = getchar()) != EOF)
    {
        printf("Got %c; enter some new data\n", c);
        fflush(stdin);
    }
    if ((c = getchar()) != EOF)
        printf("Got %c\n", c);

    return 0;
}

Example output

示例输出

$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$

This output was obtained on both Ubuntu 14.04 LTS and Mac OS X 10.11.2. To my understanding, it contradicts what the Linux manual says. If the fflush(stdin)operation worked, I would have to type a new line of text to get information for the second getchar()to read.

此输出是在 Ubuntu 14.04 LTS 和 Mac OS X 10.11.2 上获得的。据我了解,它与 Linux 手册所说的相矛盾。如果fflush(stdin)操作成功,我将不得不输入一个新的文本行来获取第二个getchar()阅读的信息。

Given what the POSIX standard says, maybe a better demonstration is needed, and the Linux documentation should be clarified.

鉴于 POSIX 标准所说的,也许需要更好的演示,并且应该澄清 Linux 文档。

demo-fflush2.c

demo-fflush2.c

#include <stdio.h>

int main(void)
{
    int c;
    if ((c = getchar()) != EOF)
    {
        printf("Got %c\n", c);
        ungetc('B', stdin);
        ungetc('Z', stdin);
        if ((c = getchar()) == EOF)
        {
            fprintf(stderr, "Huh?!\n");
            return 1;
        }
        printf("Got %c after ungetc()\n", c);
        fflush(stdin);
    }
    if ((c = getchar()) != EOF)
        printf("Got %c\n", c);

    return 0;
}

Example output

示例输出

Note that /etc/passwdis a seekable file. On Ubuntu, the first line looks like:

请注意,这/etc/passwd是一个可查找的文件。在 Ubuntu 上,第一行如下所示:

root:x:0:0:root:/root:/bin/bash

On Mac OS X, the first 4 lines look like:

在 Mac OS X 上,前 4 行如下所示:

##
# User Database
# 
# Note that this file is consulted directly only when the system is running

In other words, there is commentary at the top of the Mac OS X /etc/passwdfile. The non-comment lines conform to the normal layout, so the rootentry is:

换句话说,Mac OS X/etc/passwd文件的顶部有注释。非注释行符合正常布局,所以root入口为:

root:*:0:0:System Administrator:/var/root:/bin/sh

Ubuntu 14.04 LTS:

Ubuntu 14.04 LTS:

$ ./demo-fflush2 < /etc/passwd
Got r
Got Z after ungetc()
Got o
$ ./demo-fflush2
Allotrope
Got A
Got Z after ungetc()
Got B
$

Mac OS X 10.11.2:

Mac OS X 10.11.2:

$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$

The Mac OS X behaviour ignores (or at least seems to ignore) the fflush(stdin)(thus not following POSIX on this issue). The Linux behaviour corresponds to the documented POSIX behaviour, but the POSIX specification is far more careful in what it says — it specifies a file capable of seeking, but terminals, of course, do not support seeking. It is also much less useful than the Microsoft specification.

Mac OS X 行为忽略(或至少似乎忽略)fflush(stdin)(因此在此问题上不遵循 POSIX)。Linux 行为对应于已记录的 POSIX 行为,但 POSIX 规范在其表述上要谨慎得多——它指定了一个能够搜索的文件,但终端当然不支持搜索。它也远不如 Microsoft 规范有用。

Summary

概括

Microsoft documents the behaviour of fflush(stdin). Apparently, it works as documented on the Windows platform, using the native Windows compiler and C runtime support libraries.

Microsoft 记录了fflush(stdin). 显然,它按照 Windows 平台上的文档工作,使用本机 Windows 编译器和 C 运行时支持库。

Despite documentation to the contrary, it does not work on Linux when the standard input is a terminal, but it seems to follow the POSIX specification which is far more carefully worded. According to the C standard, the behaviour of fflush(stdin)is undefined. POSIX adds the qualifier 'unless the input file is seekable', which a terminal is not. The behaviour is not the same as Microsoft's.

尽管有相反的文档,当标准输入是终端时,它在 Linux 上不起作用,但它似乎遵循 POSIX 规范,该规范措辞要谨慎得多。根据 C 标准, 的行为fflush(stdin)是未定义的。POSIX 添加了限定符“除非输入文件是可搜索的”,而终端则不是。这种行为与微软的不同。

Consequently, portable code does not use fflush(stdin). Code that is tied to Microsoft's platform may use it and it will work, but beware the portability issues.

因此,可移植代码不使用fflush(stdin). 绑定到 Microsoft 平台的代码可能会使用它并且它会工作,但要注意可移植性问题。

POSIX way to discard unread terminal input from a file descriptor

从文件描述符中丢弃未读终端输入的 POSIX 方法

The POSIX standard way to discard unread information from a terminal file descriptor (as opposed to a file stream like stdin) is illustrated at How can I flush unread data from a tty input queue on a Unix system. However, that is operating below the standard I/O library level.

stdin如何从 Unix 系统上的 tty 输入队列中刷新未读数据中说明了从终端文件描述符(与类似的文件流相反)丢弃未读信息的 POSIX 标准方法。但是,这是在标准 I/O 库级别下运行的。

回答by tiftik

According to the standard, fflushcan only be used with output buffers, and obviously stdinisn't one. However, somestandard C libraries provide the use of fflush(stdin)as an extension. In that case you can use it, but it will affect portability, so you will no longer be able to use any standards-compliant standard C library on earth and expect the same results.

根据标准,fflush只能与输出缓冲区一起使用,显然stdin不是一个。但是,一些标准 C 库提供了fflush(stdin)作为扩展的使用。在这种情况下,您可以使用它,但它会影响可移植性,因此您将无法再使用地球上任何符合标准的标准 C 库并期望得到相同的结果。

回答by Steve Summit

I believe you should nevercall fflush(stdin), for the simple reason that you should never even find it necessary to try to flush input in the first place. Realistically, there is only one reason you might think you had to, and that is: to get past some bad input that scanfis stuck on.

我相信你永远不应该调用fflush(stdin),原因很简单,你甚至不应该首先发现有必要尝试刷新输入。实际上,您可能认为必须这样做的原因只有一个,那就是:克服一些scanf被卡住的错误输入。

For example, you might have a program that is sitting in a loop reading integers using scanf("%d", &n), and you've discovered that the first time the user types a non-digit character like 'x', the program goes into an infinite loop.

例如,您可能有一个程序在循环中使用 读取整数scanf("%d", &n),并且您发现用户第一次键入非数字字符(如 )时'x'程序会进入无限循环

When faced with this situation, I believe you basically have three choices:

面对这种情况,相信你基本上有三个选择:

  1. Flush the input somehow (if not by using fflush(stdin), then by calling getcharto read characters until \n, as is often recommended).
  2. Tell the user not to type non-digit characters when digits are expected.
  3. Use something other than scanfto read input.
  1. 以某种方式刷新输入(如果不是使用fflush(stdin),则通过调用getchar读取字符直到\n,这是通常的建议)。
  2. 告诉用户在需要数字时不要输入非数字字符。
  3. 使用除scanf读取 input以外的其他内容

Now, if you're a beginner, scanfseemslike the easiest way to read input, and so choice #3 is scary and difficult. But #2 seems like a real cop-out, because everyone knows that user-unfriendly computer programs are a problem, so it'd be nice to do better. So all too many beginning programmers get painted into a corner, feeling that they have no choice but to do #1. They more or less have to do input using scanf, meaning that it will get stuck on bad input, meaning that they have to figure out a way to flush the bad input, meaning that they're sorely tempted to use fflush(stdin).

现在,如果您是初学者,这scanf似乎是阅读输入的最简单方法,因此选择 #3 既可怕又困难。但是#2 似乎是一个真正的逃避,因为每个人都知道对用户不友好的计算机程序是一个问题,所以最好做得更好。因此,太多的新手程序员陷入了困境,觉得他们别无选择,只能做 #1。他们或多或少必须使用 进行输入scanf,这意味着它会卡在错误输入上,这意味着他们必须想办法清除错误输入,这意味着他们非常想使用fflush(stdin).

I would like to encourage all beginning programmers out there to make a different set of tradeoffs:

我想鼓励所有刚入门的程序员做出不同的权衡:

  1. During the earliest stages of your C programming career, before you're comfortable using anything other than scanf, just don't worry about bad input. Really. Go ahead and use cop-out #2 above. Think about it like this: You're a beginner, there are lots of things you don't know how to do yet, and one of the things you don't know how to do yet is: deal gracefully with unexpected input.

  2. As soon as you can, learn how to do input using functions other than scanf. At that point, you can start dealing gracefully with bad input, and you'll have many more, much better techniques available to you, that won't require trying to flush the bad input at all.

  1. 在 C 编程生涯的早期阶段,在您习惯使用 之外的任何东西之前scanf不要担心错误的输入。真的。继续使用上面的 cop-out #2。可以这样想:你是一个初学者,有很多事情你还不知道怎么做,而你还不知道怎么做的事情之一是:优雅地处理意外的输入。

  2. 尽快学习如何使用scanf. 那时,您可以开始优雅地处理错误输入,并且您将拥有更多、更好的技术可用,而根本不需要尝试刷新错误输入。

Or, in other words, beginners who are still stuck using scanfshould feel free to use cop-out #2, and when they're ready they should graduate from there to technique #3, and nobody should be using technique #1 to try to flush input at all (and certainly not with fflush(stdin).

或者,换句话说,仍然坚持使用的初学者scanf应该随意使用 cop-out #2,当他们准备好时,他们应该从那里毕业到技术 #3,没有人应该使用技术 #1 来尝试完全刷新输入(当然不是fflush(stdin).

回答by Ryan Chen

Quote from POSIX:

来自POSIX 的报价:

For a stream open for reading, if the file is not already at EOF, and the file is one capable of seeking, the file offset of the underlying open file description shall be set to the file position of the stream, and any characters pushed back onto the stream by ungetc() or ungetwc() that have not subsequently been read from the stream shall be dis- carded (without further changing the file offset).

对于打开读取的流,如果文件不在 EOF 处,并且该文件是可以查找的,则底层打开文件描述的文件偏移量应设置为该流的文件位置,任何字符被推回由 ungetc() 或 ungetwc() 到流上的那些随后没有从流中读取的文件将被丢弃(不进一步更改文件偏移量)。

Note that terminal is not capable of seeking.

请注意,终端无法搜索。

回答by Steve Summit

Using fflush(stdin)to flush input is kind of like dowsing for waterusing a stick shaped like the letter "S".

使用fflush(stdin)冲洗输入有点像使用形状像字母“S”的棒来寻找水

And helping people to flush input in some "better" way is kind of like rushing up to an S-stick dowser and saying "No, no, you're doing it wrong, you need to use a Y-shaped stick!".

帮助人们以某种​​“更好”的方式刷新输入有点像冲到 S-stick dowser 说“不,不,你做错了,你需要使用 Y 形杆!”。

In other words, the real problem isn't that fflush(stdin)doesn't work. Calling fflush(stdin)is a symptom of an underlying problem. Why are you having to "flush" input at all? That'syour problem.

换句话说,真正的问题fflush(stdin)不是不起作用。呼叫fflush(stdin)是潜在问题的征兆。为什么你必须“刷新”输入? 那是你的问题。

And, usually, that underlying problem is that you're using scanf, in one of its many confusing modes that unexpectedly leaves newlines or other whitespace on the input. The best long-term answer, therefore, is to learn how to do input using better techniques than scanf.

而且,通常,潜在的问题是您正在使用scanf, 在其许多令人困惑的模式之一中意外地在输入上留下换行符或其他空格。因此,最好的长期答案是学习如何使用比scanf.