因此,谷歌快速搜索fflush(stdin)清除输入缓冲区会发现许多网站警告不要使用它.然而,这正是我的CS教授教授课程的原因.
使用有多糟糕fflush(stdin)?即使我的教授正在使用它并且似乎完美无缺地工作,我是否真的应该放弃使用它?
Eli*_*sky 67
简单:这是未定义的行为,因为fflush它意味着在输出流上调用.这是C标准的摘录:
int fflush(FILE*ostream);
ostream指向输入流或未输入最近操作的更新流,fflush函数导致该流的任何未写入数据被传递到主机环境以写入文件; 否则,行为未定义.
所以这不是一个"多么糟糕"的问题.显然fflush(stdin)是错的,你永远不能使用它.
Jon*_*ler 36
将注释转换为答案 - 并在问题再次出现时扩展它们.
fflush(stdin)未定义的行为在POSIX,C和C++标准fflush()明确规定,该行为是不确定的,但它们都没有防止系统定义它.
ISO/IEC 9899:2011 - C11标准 - 说:
§7.21.5.2fflush功能
2如果
stream指向输入流或未输入最新操作的更新流,则该fflush函数会将该流的任何未写入数据传送到主机环境以写入该文件; 否则,行为未定义.
POSIX主要遵循C标准,但它确实将此文本标记为C扩展名.
[CX]对于打开读取的流,如果文件尚未处于EOF,并且文件是能够搜索的文件,则底层打开文件描述的文件偏移量应设置为流的文件位置,并且将被丢弃的字符
ungetc()或者ungetwc()之后未从流中读取的字符将被丢弃(不再进一步改变文件偏移量).
注意终端不能寻求; 管道或插座都不是.
fflush(stdin)Microsoft和Visual Studio运行时定义了fflush()在输入流上定义行为.
如果流已打开以进行输入,则
fflush清除缓冲区的内容.
Cygwin是一个相当常见的平台的例子,它
fflush(stdin)没有清除输入.
这就是为什么我的评论的答案版本注意到"Microsoft和Visual Studio运行时" - 如果您使用非Microsoft C运行时库,您看到的行为取决于该库.
令人惊讶的是,Linux名义上也记录了它的行为fflush(stdin),甚至以同样的方式定义它(奇迹的奇迹).
对于输入流,
fflush()丢弃从底层文件中提取但尚未被应用程序使用的任何缓冲数据.
我仍然有点困惑,并对Linux文档说它fflush(stdin)会起作用感到惊讶.尽管有这样的建议,但它通常不适用于Linux.我刚检查了Ubuntu 14.04 LTS的文档; 它说上面引用的内容,但根据经验,它不起作用 - 至少当输入流是一个不可搜索的设备,如终端时.
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;
}
Run Code Online (Sandbox Code Playgroud)
$ ./demo-fflush
Alliteration
Got A; enter some new data
Got l
$
Run Code Online (Sandbox Code Playgroud)
此输出是在Ubuntu 14.04 LTS和Mac OS X 10.11.2上获得的.据我所知,它与Linux手册所说的相矛盾.如果fflush(stdin)操作有效,我将不得不输入一行新文本来获取第二行的信息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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这/etc/passwd是一个可搜索的文件.在Ubuntu上,第一行看起来像:
root:x:0:0:root:/root:/bin/bash
Run Code Online (Sandbox Code Playgroud)
在Mac OS X上,前4行看起来像:
##
# User Database
#
# Note that this file is consulted directly only when the system is running
Run Code Online (Sandbox Code Playgroud)
换句话说,Mac OS X /etc/passwd文件的顶部有评论.非注释行符合正常布局,因此root条目为:
root:*:0:0:System Administrator:/var/root:/bin/sh
Run Code Online (Sandbox Code Playgroud)
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
$
Run Code Online (Sandbox Code Playgroud)
Mac OS X 10.11.2:
$ ./demo-fflush2 < /etc/passwd
Got #
Got Z after ungetc()
Got B
$
Run Code Online (Sandbox Code Playgroud)
Mac OS X行为忽略(或至少似乎忽略)fflush(stdin)(因此在此问题上不遵循POSIX).Linux行为对应于记录的POSIX行为,但POSIX规范在其所说的内容中要小心得多 - 它指定了一个能够搜索的文件,但终端当然不支持搜索.它也没有微软规范那么有用.
Microsoft记录了该行为fflush(stdin).显然,它使用本机Windows编译器和C运行时支持库,在Windows平台上记录.
尽管有相反的文档,但当标准输入是一个终端时,它在Linux上不起作用,但它似乎遵循POSIX规范,这更加谨慎.根据C标准,行为fflush(stdin)是不确定的.POSIX添加限定符'除非输入文件是可搜索的',终端不是.这种行为与微软的行为不同.
因此,便携式代码不使用fflush(stdin).与Microsoft平台相关的代码可能会使用它并且它可以工作,但要注意可移植性问题.
从终端文件描述符中丢弃未读信息的POSIX标准方法(与文件流相反stdin)如下所示:如何从Unix系统上的tty输入队列中清除未读数据.但是,它在标准I/O库级别以下运行.
Ste*_*mit 12
我相信你永远不应该调用fflush(stdin),原因很简单,你甚至不应该首先发现有必要尝试刷新输入。实际上,您可能认为必须刷新输入的原因只有一个,那就是:绕过一些scanf卡住的错误输入。
例如,您可能有一个使用scanf("%d", &n). 很快您就会发现,当用户第一次键入非数字字符时'x',程序会进入无限循环。
面对这种情况,相信你基本上有三个选择:
fflush(stdin),则通过调用getchar循环来读取字符直到\n,这通常是推荐的)。scanf读取 input以外的其他内容。现在,如果您是初学者,这scanf 似乎是读取输入的最简单方法,因此选项 #3 看起来既可怕又困难。但是#2 似乎是一个真正的逃避,因为每个人都知道对用户不友好的计算机程序是一个问题,所以最好做得更好。因此,太多的新手程序员陷入了困境,觉得他们别无选择,只能做 #1。他们或多或少必须使用 进行输入scanf,这意味着它会卡在错误输入上,这意味着他们必须想办法清除错误输入,这意味着他们非常想使用fflush(stdin).
我想鼓励所有初学 C 程序员做出不同的权衡:
在 C 编程生涯的早期阶段,在您习惯使用 之外的任何东西之前scanf,不要担心错误的输入。真的。继续使用上面的 cop-out #2。可以这样想:你是一个初学者,有很多事情你还不知道怎么做,而你还不知道怎么做的事情之一是:优雅地处理意外的输入。
尽快学习如何使用scanf. 在这一点上,您可以开始优雅地处理错误输入,并且您将拥有更多、更好的技术可用,而根本不需要尝试“刷新错误输入”。
或者,换句话说,仍然坚持使用的初学者scanf应该随意使用 cop-out #2,当他们准备好时,他们应该从那里毕业到技术 #3,没有人应该使用技术 #1 来尝试完全刷新输入 - 当然不是fflush(stdin).
使用fflush(stdin)冲洗输入有点像使用形状像字母“S”的棒来寻找水。
帮助人们以某种“更好”的方式刷新输入有点像冲到 S-stick dowser 说“不,不,你做错了,你需要使用 Y 形杆!”。
换句话说,真正的问题fflush(stdin)不是不起作用。呼叫fflush(stdin)是潜在问题的征兆。为什么你必须“刷新”输入? 那是你的问题。
而且,通常,潜在的问题是您正在使用scanf, 在其许多无用的模式之一中意外地在输入中留下换行符或其他“不需要的”文本。因此,最好的长期解决方案是学习如何使用比 更好的技术进行输入scanf,这样您就完全不必处理未处理的输入和其他特性。
现有的答案都没有指出问题的关键方面。
如果您发现自己想要“清除输入缓冲区”,那么您可能正在编写一个命令行交互式程序,更准确地说,您想要的是丢弃当前输入行中尚未删除的字符还没有读过。
这不是fflush(stdin)做的事。fflush支持在输入流上 使用的 C 库将其记录为不执行任何操作,或者丢弃已从底层文件读取但未传递到应用程序的缓冲数据。这很容易比当前行的其余部分输入更多或更少。在很多情况下,它可能会意外地工作,因为终端驱动程序(在默认模式下)一次一行地向命令行交互程序提供输入。然而,当您尝试从磁盘上的实际文件向程序提供输入时(可能是为了自动化测试),内核和 C 库将切换到以大“块”(通常为 4 到 8 kB)缓冲数据,而不会产生任何影响。与行边界的关系,您会想知道为什么您的程序正在处理文件的第一行,然后跳过几十行并在下面一些明显随机的行的中间进行拾取。或者,如果您决定在手写的很长的一行上测试您的程序,那么终端驱动程序将无法立即为程序提供整行,也fflush(stdin)不会跳过所有行。
那么你应该做什么呢?我更喜欢的方法是,如果您一次处理一行输入,则一次读取一整行。C 库有专门用于此目的的函数:(fgets在 C90 中,因此完全可移植,但仍然使您可以分块处理很长的行)和getline(POSIX 特定的,但将为malloc您管理一个 ed 缓冲区,以便您可以处理所有长行立即,无论他们需要多长时间)。通常存在从直接从 stdin 处理“当前行”的代码到处理包含“当前行”的字符串的代码的直接转换。
| 归档时间: |
|
| 查看次数: |
41488 次 |
| 最近记录: |