【问题标题】:Why we flush a stream but not a buffer?为什么我们刷新流而不刷新缓冲区?
【发布时间】:2015-05-29 22:55:24
【问题描述】:

我知道这将是一个非常愚蠢的问题,但是在阅读了这么多关于整个“缓冲区”系统的文档后,我不明白为什么人们会刷新流而不刷新缓冲区。

我见过人们这样写:

FILE* file=fopen("mytext.txt","wr");
char buffer[10]="";
setbuf(file,buffer);

//do some stuff....

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

所以我想知道,既然我们实际上将东西存储在缓冲区中,为什么我们刷新它关联的流而不是直接刷新缓冲区,实际上存储了一些东西并且应该被刷新。(好吧,有些人告诉我,如果事情像我说的那样,那就是同样的事情,所以麻烦我自己......)

例如,我们不能写fflush(buffer)之类的东西。为什么?

【问题讨论】:

  • 在你家,你说的是“冲粪”还是“冲马桶”? :-) 冲洗是厕所/溪流的属性,而不是(审查的)/数据的属性。
  • 不相关:"wr"??,应该是char buffer[BUFSIZ];
  • @milleniumbug "wr""rw" 都是无效的文件模式。
  • @BLUEPIXY 是的,忽略我之前的评论。

标签: c++ c buffer fflush


【解决方案1】:

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

所以刷新函数需要知道要复制的源和目标。

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

对于 C 风格的 <cstdio>,如果您只想使用一个参数刷新,FILE* 或 char 数组需要知道它应该复制到的文件。

您的缓冲区是一个哑数组,它只存储用于读取/写入的数据。由于那里没有其他信息,因此获取指向缓​​冲区的指针的函数无法知道将其写入的目的地 - 所以想象中的 fflush 调用看起来像 fflush(buffer, file); ,它不会让你到任何地方。另一方面,FILE* 存储指向缓冲区的指针(您使用 setbuf(file,buffer) 函数调用设置指针)。

【讨论】:

  • 其实milleniumbug是对的。问题是询问 stdio.h 函数,而不是 C++ 流,这与问题标题所暗示的相反。代码示例清楚地表明缓冲区只是一个 char * 指针,问题指的是 setbuf 和 fflush 函数。这个指针确实不知道文件。
  • @JerryCoffin 稍微改写了答案(“流”、“缓冲区”和“文件”这几个词太草率了)。
  • @DavidRoundy:啊,好点子。我对标签的关注太多,对示例代码关注不够。
【解决方案2】:

以下内容仅涉及 iostream 及其缓冲区对象。有关与 C 样式 I/O 相关的缓冲区的信息,请参阅 @milleniumbug 的回答。

主要是因为如果尝试刷新底层缓冲区失败,您(至少通常)希望设置流的坏位。

流在与底层流交互时还使用了一个稍微复杂的小舞蹈,其中流创建了一个哨兵对象,然后执行一个动作,然后哨兵对象被销毁。哨兵旨在使前缀和后缀操作异常安全。

所以整体顺序是这样的:

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:

basic_ostream&flush();

效果:表现为未格式化的输出函数(如 27.7.3.6.1 第 1 段所述)。如果 rdbuf() 不是空指针,则构造一个哨兵对象。如果此对象在转换为 bool 类型的值时返回 true,则函数调用 rdbuf()->pubsync()。如果该函数返回 -1 调用 setstate(badbit) (可能会抛出 ios_base::failure (27.5.5.4))。否则,如果哨兵对象返回 false,则不执行任何操作。

返回:*this。

...和 ​​[ofstream.cons]/2:

显式 basic_ofstream(const char* s, ios_base::openmode 模式 = ios_base::out);

效果:构造类basic_ofstream的对象,用basic_ostream(&sb)初始化基类,用basic_filebuf())初始化sb(27.7.3.2, 27.9.1.2),然后调用rdbuf()->open(s,模式|ios_base::out)。如果该函数返回一个空指针,则调用 setstate(failbit)。

【讨论】:

    【解决方案3】:

    我不明白为什么人们会刷新流而不刷新缓冲区。

    因为缓冲区不知道它应该被刷新到哪里。流可以。

    FILE* file=fopen("mytext.txt","wr");
    setbuf(file,buffer);
    

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

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

    char buffer[BUFSIZ]="";
    

    所以我想知道,既然我们实际上在缓冲区中存储了东西,为什么我们要刷新它关联的流而不是直接刷新缓冲区,因为缓冲区实际上存储了一些东西并且应该被刷新。

    因为缓冲区只是数据存储。它对数据的使用方式一无所知。流知道它的缓冲区正在用于什么(缓存写入的数据),以及它需要刷新到哪里(相关文件)。因此,您必须刷新流,以便它可以利用该信息。

    例如,我们不能写 fflush(buffer) 之类的东西。为什么?

    您希望缓冲区将其内容刷新到哪里?它没有这样的信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-14
      • 2011-08-26
      • 2022-10-02
      • 2013-02-09
      • 2018-07-11
      • 1970-01-01
      • 2020-04-06
      • 1970-01-01
      相关资源
      最近更新 更多