缓冲和未缓冲的流

Buffered and unbuffered stream

本文关键字:缓冲      更新时间:2023-10-16

在缓冲流的情况下,它在一本书中说它等到缓冲区已满后再写回监视器。例如:

cout << "hi"; 
  1. 他们所说的"缓冲区已满"是什么意思。

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

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

你的书似乎不是很有帮助。

1(输出流将其字节发送到std::streambuf,该可能包含缓冲区;使用的std::filebuf(派生自streambuf并且std::ofstream通常会被缓冲。 这意味着当你输出一个字符,它不一定立即输出;它将写入缓冲区,并仅在缓冲区完整,或者您以某种方式明确请求它,通常通过调用 flush()流上(直接或间接地使用 std::endl (。但是,这可能会有所不同;输出到std::coutstdout,大多数实现或多或少会遵循 stdout 对于std::cout,如果输出,则更改缓冲策略将转到交互式设备。

无论如何,如果您不确定,并且希望确保输出确实会离开您的程序,只需添加一个调用即可刷新。

2(你的书在这里是错误的。

其中一个缓冲策略是 unitbuf ;这是 您可以设置或重置std::ostream(std::ios_base::set()std::ios_base::unset()std::ios_basestd::ostream ,因此您可以在std::ostream上调用这些函数对象(。 设置unitbuf后,std::ostream会添加对flush()的调用到每个输出函数的末尾,所以当你写:

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

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

请注意,这里所讨论的只是streambuf中的缓冲区。通常,操作系统还会缓冲。 刷新缓冲区所做的所有操作都是将字符传输到操作系统;这一事实意味着您不能使用 ofstream直接在需要事务完整性时。

3( 当您使用 >> 输入字符串或字符缓冲区时, std::istream首先跳过前导空格,然后输入但不包括下一个空格。 在正式条款中标准,它从流中"提取"字符,以便它们不会再被看到(除非您寻求,如果流支持它(。下一个输入将从上一个停止的地方拾取。 是否以下字符在缓冲区中,或者仍在磁盘上,真的是不切题的。

请注意,输入的缓冲有些复杂,因为它会发生在几个不同的级别,在操作系统级别,它需要不同的形式取决于设备。 通常,操作系统将通过以下方式缓冲文件部门,经常提前阅读几个部门。 操作系统将始终返回所需数量的字符,除非遇到结尾文件。 大多数操作系统将按行缓冲键盘:不从读取请求,直到输入完整行,并且从不返回读取请求中当前行尾以外的字符。

std::ostream使用streambuf进行输出的方式相同, std::istream使用一个来获取每个单独的角色。 在这种情况下std::cin,它通常是一个filebuf;当istream请求一个字符,filebuf如果它有一个;如果没有,它将尝试重新填充缓冲区,请求例如 512(或其缓冲区大小(字符操作系统。 它将根据其缓冲策略进行响应设备,如上所述。

无论如何,如果std::cin连接到键盘,并且您已经键入"hello world",将读取您键入的所有字符最终在溪流旁。 (但是如果你使用>>,会有很多您不会看到的空白。

C++中的流是缓冲区以提高效率,也就是说,与内存操作相比,文件和控制台IO非常慢。

为了解决这个问题C++流有一个缓冲区(内存库(,其中包含要写入文件或输出的所有内容,当它已满时,它会刷新到文件。输入则相反,当缓冲区耗尽时,它会获取更多。

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

std::cout << 1 << "hello" << ' ' << "worldn";

将是 4 次写入一个效率低下的文件。

但是,在 std::cout、cin 和 cerr 的情况下,这些类型实际上默认关闭了缓冲,以确保它可以与 std::p rintf 和 std::p uts 等结合使用。

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

std::ios_base::sync_with_stdio(false);

但是不要在设置错误时使用 C 样式控制台输出,否则可能会发生不好的事情。

您可以使用一个小应用程序自己查看差异。

#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-语句在我的装备上大约需要 2 秒,您可能需要调整您的 i <条件。>

1("缓冲区已满"是什么意思。

对于缓冲输出,有一个内存区域,称为缓冲区,您写出的内容在实际写入输出之前被存储。当您说cout << "hi"字符串可能只复制到该缓冲区中,而不是写出。cout 通常等到内存填满后才真正开始写出内容。

这样做的

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

2(它在我的书中说,发送到CERR的所有内容都会立即写入标准错误设备,这是否意味着它发送"H"然后"I"......?

它只是意味着不使用缓冲区。 cerr 可能仍会同时发送"h"和"i",因为它已经同时拥有它们。

3(在这个例子中,ch将被分配给"hello",而"world"将被忽略,这是否意味着它仍然在缓冲区中,它会影响未来语句的结果?

这与缓冲没有任何关系。 运算符 char*>>定义为读取直到看到空格,因此它停止在"hello"和"world"之间的空格处。但是,是的,下次阅读时,您将获得"世界"。

(尽管如果该代码不仅仅是 actuall 代码的释义,那么它具有未定义的行为,因为您正在将文本读取到未定义的内存位置。相反,您应该执行以下操作:

std::string s;
cin >> s;

(

  1. 每次调用写入终端的速度都很慢,因此为了避免执行缓慢的操作,通常会将数据存储在内存中,直到输入了一定数量的数据或使用 fflush 或 std::endl 手动刷新缓冲区。这样做的结果是,有时文本可能不会在您期望的时刻写入终端。

  2. 由于错误消息的计时比正常输出更为关键,因此将忽略性能影响,并且不会缓冲数据。但是,由于字符串是在单个数据片段中传递的,因此它是在一次调用中写入的(在某处的循环中(。

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