6

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

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

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

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

4

5 回答 5

15

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

1) 输出流将它们的字节发送到 a std::streambuf,其中可能包含一个缓冲区;使用的std::filebuf(派生自streambuf)并且std::ofstream通常会被缓冲。这意味着当你输出一个字符时,它不一定会立即输出;它将被写入缓冲区,并仅在缓冲区已满时输出到操作系统,或者您以某种方式显式请求它,通常通过调用 flush()流(直接或间接,通过使用std::endl)。但是,这可能会有所不同;输出 tostd::cout与 同步 stdout,大多数实现或多或少遵循 stdoutfor的规则,std::cout如果输出到交互式设备,则更改缓冲策略。

无论如何,如果您不确定,并且想确定输出确实离开了您的程序,只需添加对 flush 的调用。

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. 通常,操作系统也会缓冲。所有刷新缓冲区所做的都是将字符传输到操作系统;这个事实意味着您不能 ofstream在需要事务完整性时直接使用。

3)当您使用 输入到字符串或字符缓冲区>>时, std::istream首先跳过前导空格,然后输入最多但不包括下一个空格。在标准的正式术语中,它从流中“提取”字符,这样它们就不会再被看到(除非你寻找,如果流支持的话)。下一个输入将在前一个停止的地方拾取。以下字符是在缓冲区中还是仍在磁盘上,这实际上无关紧要。

请注意,输入的缓冲有点复杂,因为它发生在几个不同的级别,并且在操作系统级别,它根据设备采用不同的形式。通常,操作系统会按扇区缓冲文件,通常会提前读取几个扇区。除非遇到文件结尾,否则操作系统将始终返回所需数量的字符。大多数操作系统将逐行缓冲键盘:在输入完整行之前不会从读取请求返回,并且永远不会在读取请求中返回超出当前行末尾的字符。

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

无论如何,如果std::cin连接到键盘,并且您输入了 ,您输入"hello world"的所有字符最终都会被流读取。(但如果你使用>>,会有很多看不到的空白。)

于 2012-05-09T15:25:33.317 回答
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 风格的控制台输出,而它被设置为 false 或可能会发生不好的事情。

于 2012-05-09T15:01:51.113 回答
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 语句大约需要 2 秒,您可能需要调整您的 i < 条件。

于 2012-05-09T15:13:43.357 回答
1

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

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

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

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

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

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

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

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

std::string s;
cin >> s;

)

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

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

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

于 2012-05-09T15:10:06.860 回答