缓冲和无缓冲流

时间:2012-05-09 14:53:26

标签: c++ stream buffered unbuffered

如果是缓冲流,它会在书中说它等到缓冲区已满,然后写回监视器。例如:

cout << "hi"; 
  1. “缓冲区已满”是什么意思。

    cerr << "hi";
    
  2. 在我的书中说,发送到cerr的所有内容都会立即写入标准错误设备,这是什么意思?

    char *ch;
    cin>> ch; // I typed "hello world";
    
  3. 在这个示例中,ch将被分配给“hello”并且“world”将被忽略是否意味着它仍然在缓冲区中并且它将影响未来语句的结果?

5 个答案:

答案 0 :(得分:10)

你的书似乎没什么帮助。

1)输出流将其字节发送到std::streambuf,这可能是 包含缓冲区;使用的std::filebuf(源自streambuf) 并且std::ofstream通常会被缓冲。这意味着什么时候 你输出一个字符,它不一定立即输出;它会 写入缓冲区,仅在缓冲区输出时才输出到OS 完全,或者您通过调用以某种方式明确请求它 流上的flush()(直接或间接使用std::endl)。 然而,这可能会有所不同;输出到std::cout与...同步 stdout,大多数实现都会或多或少地遵循规则 stdoutstd::cout,如果输出则更改缓冲策略 是一个互动设备。

无论如何,如果你不确定,你想确定输出 确实离开你的程序,只需添加一个冲洗呼叫。

2)你的书在这里错了。

缓冲策略之一是unitbuf;这是一面旗帜 std::ostream您可以设置或重置std::ios_base::set()std::ios_base::unset() - std::ios_base是基类 std::ostream,因此您可以在std::ostream上调用这些功能 宾语)。设置unitbuf后,std::ostream会向flush()添加呼叫 到每个输出函数的末尾,所以当你写:

std::cerr << "hello, world";

字符串中所有字符后刷新流 如果设置了unitbuf,则输出。启动时,unitbuf已设置 std::cerr;默认情况下,它不会在任何其他文件上设置。但是你 可以随意设置或取消设置。我建议反对 在std::cerr上取消设置,但如果std::cout正在输出 交互式设备,将它设置在那里是很有意义的。

请注意,此处所有问题都是streambuf中的缓冲区。 通常,OS也会缓冲。所有冲洗缓冲区都是 将字符传输到操作系统;这个事实意味着你无法使用 {Q}需要事务完整性时直接ofstream

3)当您使用>>输入字符串或字符缓冲区时, std::istream首先跳过前导空格,然后输入到 但不包括下一个空白区域。在正式的条款 标准,它从流中“提取”字符,以便它们 将不再被看到(除非你寻求,如果流支持它)。 下一个输入将从前一个左侧关闭的位置拾取。是否 以下字符在缓冲区中,或者仍然在磁盘上,实际上是 不相关的。

请注意,输入的缓冲有点复杂,因为它会发生 在几个不同的级别,在操作系统级别,它需要不同 形式取决于设备。通常,操作系统将缓冲文件 部门,往往提前阅读几个部门。操作系统将永远 返回尽可能多的字符,除非遇到结束 文件。大多数操作系统会逐行缓冲键盘:不从a返回 读取请求直到输入完整的行,并且永远不会返回 读取请求中当前行末尾之外的字符。

std::ostream使用streambuf输出的方式相同, std::istream使用一个来获取每个角色。在这种情况下 std::cin的{​​{1}},通常为filebuf;何时istream 如果请求一个字符,filebuf将从其缓冲区返回一个字符 它有一个;如果没有,它将尝试重新填充缓冲区, 请求例如512(或其任何缓冲区大小)字符来自 OS。哪个会根据其缓冲政策做出回应 设备,如上所述。

无论如何,如果std::cin连接到键盘,那么你就是 键入"hello world",将读取您键入的所有字符 最终由溪流。 (但如果您使用的是>>,那么会有很多 将看不到的空格。)

答案 1 :(得分:2)

C ++中的

流是缓冲区以提高效率,即文件和控制台IO与内存操作相比非常慢。

为了对抗这个C ++流,有一个缓冲区(一组内存),它包含要写入文件或输出的所有内容,当它已满时,它会刷新到文件中。对于输入,反之亦然,当缓冲区耗尽时,它会获得更多。

这对于流非常重要,因为以下

std::cout << 1 << "hello" << ' ' << "world\n";

对文件进行4次写入效率低下。

然而,在std :: cout,cin和cerr的情况下,这些类型实际上默认关闭缓冲,以确保它可以与std :: printf和std :: puts等一起使用...

重新启用它(我建议这样做):

std::ios_base::sync_with_stdio(false);

但是不要使用C风格的控制台输出,因为它设置为假或可能发生错误。

答案 2 :(得分:2)

您可以使用小应用程序查看差异。

#include <iostream>
int main() {
    std::cout<<"Start";
    //std::cout<<"Start"<<std::endl; // line buffered, endl will flush.
    double j = 2;
    for(int i = 0; i < 100000000; i++){
        j = i / (i+1);
    }
    std::cout<<j;
    return 0;
}

尝试两个“开始”状态的差异,然后更改为cerr。您注意到的差异是由于缓冲。

for-statement在我的装备上大约需要2秒钟,你可能需要调整i&lt;在你的条件。

答案 3 :(得分:1)

  

1)“缓冲区已满”是什么意思。

对于缓冲输出,存在一个称为缓冲区的内存区域,其中您写出的内容在实际写入输出之前存储。当你说cout << "hi"时,字符串可能只被复制到那个缓冲区而没有被写出来。 cout通常会等到内存已经填满之后才开始写出来。

之所以这样,是因为通常开始实际写入数据的过程很慢,所以如果你为每个角色都这样做,那么你的表现就会很糟糕。使用一个缓冲区,这样程序只需要不经常这样做,你就可以获得更好的性能。

  

2)它在我的书中说,发送给cerr的所有内容都立即被写入标准错误设备,这是否意味着它发送'h'然后'i'......?

这只意味着没有使用缓冲区。 cerr可能仍会同时发送'h'和'i',因为它已经同时发送了'h'和'i'。

  

3)在这个例子中ch将被分配给“hello”并且“world”将被忽略它是否意味着它仍然在缓冲区并且它将影响未来语句的结果?

这与缓冲无关。运营商&gt;&gt; for char *被定义为读取直到看到空格,因此它停在“hello”和“world”之间的空格处。但是,是的,下次你读到的时候会得到“世界”。

(虽然如果该代码不仅仅是对执行代码的解释,那么它具有未定义的行为,因为您正在将文本读入未定义的内存位置。相反,您应该这样做:

std::string s;
cin >> s;

答案 4 :(得分:0)

  1. 每次写入终端的调用都很慢,所以为了避免做慢的事情,数据会被存储在内存中,直到输入了一定数量的数据或者用fflush或std手动刷新缓冲区: :ENDL。结果有时候文本可能不会在您预期的时刻写入终端。

  2. 由于错误消息的时间比正常输出更重要,因此忽略性能命中并且不缓冲数据。但是,由于字符串是在单个数据中传递的,因此它会在一次调用中编写(在某个循环内部)。

  3. 这个世界仍然会在缓冲区中,但通过在3行程序中尝试来自己证明这一点很容易。但是,您的示例将失败,因为您尝试写入未分配的内存。您应该将输入转换为std :: string。