为什么我们刷新流而不是缓冲区?

5
我知道这可能是一个相当愚蠢的问题,但在阅读了许多关于整个“缓冲”系统的文件后,我不明白为什么人们会刷新流而不是缓冲区。
我见过人们写像这样的东西:
FILE* file=fopen("mytext.txt","wr");
char buffer[10]="";
setbuf(file,buffer);

//do some stuff....

fflush(file);
....
fclose(file);

我在想,既然我们实际上是将东西存储在缓冲区中,为什么我们会刷新与其相关的流而不是直接刷新缓冲区本身,因为缓冲区实际上存储了一些内容,并且应该被刷新。(有些人告诉我,如果按照我的方式来做,结果也会是一样的,所以我也没必要纠结…)
例如,我们无法像fflush(buffer)这样写东西。为什么?

5
在你家中,你会说“冲刷粪便物”还是“冲水马桶”?冲洗是马桶/管道的属性,而不是粪便/污物的属性。 - paxdiablo
不相关:"wr"?应该是char buffer[BUFSIZ]; - BLUEPIXY
@milleniumbug "wr"和"rw"都是无效的文件模式。 - BLUEPIXY
@BLUEPIXY 是的,请忽略我之前的评论。 - milleniumbug
3个回答

6

刷新是将数据从流的内部缓冲区复制到底层文件的操作。

因此,刷新函数需要知道源和目标以便复制。

这取决于I/O实现方式,对于C++中的<iostream>,请参见Jerry Coffin的答案 - <iostream>中的缓冲区更加智能。

对于C风格的<cstdio>,如果您想要只有一个参数进行刷新,则FILE*或字符数组需要知道它应该复制到哪个文件。

您的缓冲区是一个简单的数组,只是用于读/写数据。由于没有其他信息,因此获取指向该缓冲区的指针的函数无法知道要写入数据的目标 - 因此虚拟的fflush调用可能类似于fflush(buffer, file);,但这并没有起到作用。另一方面,FILE*存储了指向您的缓冲区的指针(您可以使用setbuf(file,buffer)函数调用设置指针)。


实际上,milleniumbug是正确的。问题是关于stdio.h函数的,而不是C++流,这与问题标题所暗示的相反。代码示例清楚地表明缓冲区只是一个char *指针,并且问题涉及setbuf和fflush函数。该指针确实不知道文件的存在。 - David Roundy
@JerryCoffin 重新措辞了答案(使用“stream”、“buffer”和“file”这些词太随意了)。 - milleniumbug
@DavidRoundy:啊,说得好。我过于关注标签,而忽略了示例代码。 - Jerry Coffin

2
以下内容仅涉及iostreams及其缓冲对象。关于与C风格I/O相关的缓冲区的信息,请参阅@milleniumbug的回答。
主要是因为(至少通常情况下),如果尝试刷新底层缓冲区失败,您希望流的badbit被设置。
此外,当流与底层流交互时,还有一个稍微复杂的小舞蹈,其中流创建一个sentry对象,然后执行操作,然后销毁sentry对象。Sentry旨在使前缀和后缀操作异常安全。
所以总体序列大致如下:
create sentry
if that succeeds (sentry converts to true) call rdbuf()->pubsync()
    if that fails (returns -1) setstate(badbit);

一个流缓冲区(例如,basic_filebuf)直接附加到底层文件系统对象上 - 实际上,iostream对象和底层文件对象之间的所有交互都是通过文件缓冲区完成的。如上所示,当流对象确实需要刷新缓冲区时,它只需要调用缓冲区的pubsync()成员函数告诉缓冲区自己刷新即可。
[供参考:ostream.unformatted/7:
作用:行为类似于未格式化输出函数(如27.7.3.6.1第1段所述)。如果rdbuf()不是空指针,则构造一个sentry对象。如果将该对象转换为bool类型的值时返回true,则函数调用rdbuf()->pubsync()。如果该函数返回-1,则调用setstate(badbit)(可能会抛出ios_base::failure异常(27.5.5.4))。否则,如果sentry对象返回false,则什么也不做。
返回值:*this。
...和ofstream.cons/2:
解释:使用给定的模式(默认为ios_base::out)构造一个basic_ofstream类对象,初始化基类为basic_ostream(&sb),并初始化sb为basic_filebuf()(27.7.3.2、27.9.1.2),然后调用rdbuf()->open(s, mode|ios_base::out)。如果该函数返回空指针,则调用setstate(failbit)。

0
我不明白为什么人们会清空流而不是缓冲区。
因为缓冲区不知道应该刷新到哪里,而流则有这个知识。
FILE* file=fopen("mytext.txt","wr");
setbuf(file,buffer);

默认情况下,FILE 结构包含指向其自身内部缓冲区的指针。 setbuf() 用调用者提供的缓冲区替换该缓冲区。但无论哪种方式,fwrite()fputs() 和其他类似函数都会将数据写入流的当前缓冲区,而 fflush() 则将该缓冲区的内容刷新到底层文件。

顺便说一句,setbuf() 要求调用者提供的缓冲区大小至少为 BUFSIZ

char buffer[BUFSIZ]="";

我在想,既然我们实际上是将东西存储在缓冲区中,为什么我们要刷新与之关联的流而不是直接刷新实际存储内容并应该被刷新的缓冲区呢?
因为缓冲区只是数据存储。它不知道数据的使用方式。流知道它的缓冲区用于什么(缓存写入的数据),以及需要刷新到哪里(相关联的文件)。所以必须刷新流以便它可以利用这些信息。
例如,我们不能编写类似fflush(buffer)的代码。为什么?
你期望缓冲区将其内容刷新到哪里?它没有此类信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接