endl并刷新缓冲区

时间:2011-01-20 20:04:16

标签: c++ buffer flush

C ++入门一书中,在第(1)章中,它提到了以下内容:

  

endl是一个特殊值,称为操纵符,当写入时   输出流具有将换行符写入输出的效果   浏览与该设备关联的缓冲区通过刷新缓冲区,我们确保用户将看到写入的输出   立即流。

“冲洗缓冲区”是什么意思?

5 个答案:

答案 0 :(得分:32)

输出通常在写入目标设备之前进行缓冲。这样,当写入慢速访问设备(如文件)时,它不必在每个字符后访问设备。

刷新意味着清空缓冲区并将其实际写入设备。

答案 1 :(得分:20)

C ++的iostream是缓冲的,这意味着当你输出到ostream时,内容不会立即转到流后面的内容,例如:在cout的情况下stdout。流的实现确定何时实际发送流的缓冲部分。这样做是出于效率的原因,逐字节写入网络或磁盘流是非常低效的,通过缓冲这个问题就解决了。

但这意味着当您将调试消息写入日志文件并且程序崩溃时,您可能会丢失通过流写入日志文件的部分数据,因为日志的一部分可能仍在流的缓冲区尚未写入实际文件。为防止这种情况发生,您需要通过显式刷新方法调用或使用endl的方便使流刷新其缓冲区。

但是,如果您只是定期写入文件,则应使用\ n而不是endl来防止流每行都不必要地刷新流,从而降低性能。

编辑包含此注释:

cin和cout有一种特殊的关系,从cin读取会事先自动冲洗cout。这确保了例如在从cin读取等待输入之前,用户实际上会看到您写入cout的提示。因此,即使在cout中你通常也不需要endl但是可以使用\ n代替。您可以通过将它们绑在一起来在其他流之间创建这种关系。

答案 2 :(得分:7)

  

"刷新缓冲区是什么意思"这里吗?

std::endl导致流的内部分段存储器中的数据("缓冲区")被刷新" (转移)到操作系统。后续行为取决于流映射到的设备类型,但通常,刷新将提供数据已物理传输到关联设备的外观。然而,突然失去力量可能会打败这种错觉。

这种冲洗涉及一些overhead(浪费的时间),因此当执行速度是一个重要问题时应该最小化。尽量减少这种开销的总体影响是data buffering的根本目的,但这个目标可以通过过度冲洗来消除。


背景资料

计算系统的I / O通常非常复杂,由多个抽象层组成。每个这样的层可能引入一定量的开销。数据缓冲是一种通过最小化在系统的两个层之间执行的单个事务的数量来减少此开销的方法。

  • CPU /内存系统级缓冲(缓存):对于非常高的活动,即使计算机的随机存取内存系统也可能成为瓶颈。为了解决这个问题,CPU通过提供多层隐藏缓存(其各个缓冲区称为缓存线)来虚拟化内存访问。这些处理器缓存缓冲了算法的内存写入(根据writing policy),以最大限度地减少内存总线上的冗余访问。

  • 应用程序级缓冲:虽然它并不总是必要的,但是应用程序分配内存块以在将输出数据传递给I之前累积输出数据的情况并不少见/ O库。这提供了允许随机访问(如果需要)的基本好处,但是这样做的一个重要原因是它最小化了与进行库调用相关的开销 - 这可能比简单地写入存储器阵列更耗时。

  • I / O库缓冲C++ IO stream library可选择为每个打开的流管理缓冲区。特别是,此缓冲区用于限制system calls到操作系统内核的数量,因为此类调用往往会产生一些非常重要的开销。 这是使用std::endl时刷新的缓冲区。

  • 操作系统内核和设备驱动程序:操作系统根据流附加到的输出设备将数据路由到特定的设备驱动程序(或子系统)。此时,实际行为可能会有很大差异,具体取决于该类型设备的性质和特征。例如,当设备是硬盘时,设备驱动程序可能启动立即传输到设备,而是维护自己的缓冲区以进一步减少冗余操作(因为磁盘也是如此)最有效地写入块中)。为了显式刷新内核级缓冲区,可能需要调用系统级函数,例如fsync() on Linux - 甚至closing关联的流,并不一定强制进行此类刷新。

    示例输出设备可能包含......

    • 本地计算机上的终端
    • 远程计算机上的终端(通过SSH或类似方式)
    • 通过pipes or sockets
    • 将数据发送到其他应用程序
    • 大量存储设备和相关文件系统的许多变体,可以(再次)通过网络本地连接或分发
  • 硬件缓冲区:特定硬件可能包含自己的内存缓冲区。例如,硬盘驱动器通常包含disk buffer,以便(除其他外)允许进行物理写入而不需要系统的CPU参与整个过程。

    < / LI>

在许多情况下,这些不同的缓冲层往往(在某种程度上)是多余的 - 因此基本上是过度的。但是,如果其他层因任何原因无法提供与每层相关的开销的最佳缓冲,则每层的缓冲可以提供巨大的吞吐量增益。

长话短说,std::endl 寻址由该特定流的C ++ IO流库管理的缓冲区。在调用std::endl之后,数据将被移动到内核级管理,接下来发生的数据取决于很多因素。


如何避免std::endl

的开销
inline std::ostream & endl( std::ostream & os )
   {
   os.put( os.widen('\n') ); // http://en.cppreference.com/w/cpp/io/manip/endl
   if ( debug_mode ) os.flush(); // supply 'debug_mode' however you want
   return os;
   }

在此示例中,您提供了一个自定义endl,可以调用或不调用对flush()的内部调用(这是强制传输到操作系统的内容)。启用刷新(使用debug_mode变量)对于调试在程序终止之前能够检查输出(例如磁盘文件)的情况非常有用,然后才能完全关闭关联的流(这将是强制最后冲洗缓冲区。)

答案 3 :(得分:1)

使用std::cout时,输出运算符(<<)之后使用的操作数存储在缓冲区中,不会显示在stdin(通常是终端或命令提示符)上直到遇到std::endlstd::cin,导致缓冲区被刷新,在某种意义上,将缓冲区的内容显示/输出到stdin

考虑这个程序:

#include <iostream>
#include <unistd.h>

int main(void)
{
    std::cout << "Hello, world";
    sleep(2);
    std::cout << std::endl;

    return 0;
}

获得的输出将是:

2秒后

你好,世界

答案 4 :(得分:0)

一个简单的代码向您展示c ++中缓冲I / O的效果

您提供的所有输入都会被缓冲,然后在输入的情况下传递给程序变量。

看看下面的代码:     //程序以测试缓冲的I / O如何对我们的程序产生意想不到的影响     #包括     使用命名空间std;

int main()
{
    int a;
    char c;
    cin>>a;
    cin>>c;
    cout<<"the number is : "<<a;
    cout<<"\nthe character is : "<<c;

}

在这里我们声明了两个变量:一个int和一个char 如果我们输入数字为“ 12d34” 这将导致int变量仅接受12作为值,并且将丢弃仍保留在缓冲区中的其余部分。 在下一个输入中,char变量将自动接受值“ d” 甚至不要求您输入任何信息