使用fflush(stdin)
因此,快速Googlesearchfflush(stdin)
清除input缓冲区会显示许多网站警告不要使用它。 然而这正是我的CS教授如何教课程去做的。
使用fflush(stdin)
有多糟? 我应该真的放弃使用它,即使我的教授正在使用它,它似乎工作完美无瑕?
简单:这是未定义的行为,因为fflush
是为了在输出stream上调用。 这是C标准的摘录:
int fflush(FILE * ostream);
ostream指向输出stream或没有input最近操作的更新stream,fflush函数会将该stream的所有未写入数据传送到主机环境以写入文件; 否则,行为是不确定的。
所以这不是“多么糟糕”的问题。 fflush(stdin)
显然是错误的 ,你永远不要使用它 。
根据标准, fflush
只能用于输出缓冲区,显然stdin
不是一个。 但是, 有些编译器提供了fflush(stdin)作为扩展。 在这种情况下,你可以使用它,但是它会影响可移植性,所以你将不再能够使用任何符合标准的编译器,并期望得到相同的结果。
将意见转换为答案 – 并且在问题定期重新出现之后进行扩展。
标准C和POSIX离开fflush(stdin)
为未定义的行为
fflush()
的POSIX ,C和C ++标准明确声明行为是未定义的,但是没有一个阻止系统定义它。
ISO / IEC 9899:2011 – C11标准 – 说:
§7.21.5.2fflush函数
¶2如果
stream
指向输出stream或没有input最近操作的更新stream,则fflush
函数会将该stream的所有未写入的数据传送到主机环境以写入文件; 否则,行为是不确定的。
POSIX主要遵循C标准,但它确实将此文本标记为C扩展。
[CX]对于打开读取的stream,如果文件不在EOF中,并且该文件是能够查找的文件,则将底层打开文件描述的文件偏移设置为stream的文件位置,并且任何
ungetc()
或ungetwc()
后面没有从stream中读取的字符将被丢弃(不会进一步改变文件偏移量)。
请注意,terminal不能寻求; pipe道和sockets也不是。
微软定义了fflush(stdin)
的行为,
Microsoft和Visual Studio运行库定义了在inputstream上定义fflush()
的行为。
如果该stream打开input,
fflush
清除缓冲区的内容。
MM 注意到 :
Cygwin是
fflush(stdin)
不清除input的一个相当普通的平台的例子。
这就是为什么我的评论注释“Microsoft和Visual Studio运行时”的答案版本 – 如果您使用的是非Microsoft C运行时库,则您看到的行为取决于该库。
Linux的文档和实践似乎相互矛盾
令人惊讶的是, Linux名义上也logging了fflush(stdin)
的行为,甚至以同样的方式(奇迹的奇迹)来定义它。
对于inputstream,
fflush()
会丢弃已经从底层文件中获取但尚未被应用程序使用的缓冲数据。
我对Linux文档说fflush(stdin)
会起作用,我仍然有些困惑和惊讶。 尽pipe有这个build议,但它通常不适用于Linux。 我刚刚检查了Ubuntu 14.04 LTS上的文档; 它说上面引用了什么,但从经验上来说,它不起作用 – 至less当inputstream是一个不可search的设备,如terminal。
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; }
输出示例
$ ./demo-fflush Alliteration Got A; enter some new data Got l $
这个输出是在Ubuntu 14.04 LTS和Mac OS X 10.11.2上获得的。 据我的理解,这与Linux手册所说的相矛盾。 如果fflush(stdin)
操作起作用,我将不得不input新的一行文本来获取第二个getchar()
信息。
鉴于POSIX标准所说的,也许需要更好的演示,并且Linux文档应该被阐明。
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; }
输出示例
请注意, /etc/passwd
是一个可search的文件。 在Ubuntu上,第一行看起来像:
root:x:0:0:root:/root:/bin/bash
在Mac OS X上,前4行看起来像:
## # User Database # # Note that this file is consulted directly only when the system is running
换句话说,在Mac OS X /etc/passwd
文件的顶部有评论。 非注释行符合正常布局,所以root
条目是:
root:*:0:0:System Administrator:/var/root:/bin/sh
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:
$ ./demo-fflush2 < /etc/passwd Got # Got Z after ungetc() Got B $
Mac OS X行为忽略(或者至less似乎忽略) fflush(stdin)
(因此在这个问题上不遵循POSIX)。 Linux的行为对应于已经logging的POSIX行为,但是POSIX规范在说明上要小心得多 – 它指定了一个能够search的文件,但terminal当然不支持查找。 它也比微软规范less得多。
概要
Microsoftloggingfflush(stdin)
的行为。 显然,它使用本地Windows编译器和C运行时支持库在Windows平台上logging。
尽pipe有相反的文档,但在标准input是terminal的情况下,它在Linux上不起作用,但似乎遵循POSIX规范,这是更加谨慎的措辞。 根据C标准, fflush(stdin)
的行为是未定义的。 POSIX添加限定符'除非input文件是可search的',terminal不是。 行为与微软的不一样。
因此,可移植代码不使用fflush(stdin)
。 与微软平台绑定的代码可能会使用它,它会工作,但要小心可移植性问题。
POSIX方式丢弃来自文件描述符的未读terminalinput
如何从Unix系统上的ttyinput队列中刷新未读数据,说明POSIX标准的从terminal文件描述符(而不是stdin
等文件stream)丢弃未读信息的方法。 但是,这是在标准I / O库级别下运行的。