C/C++ 输入输出缓冲区在Windows和Linux下对比

C++中cin、cout,cerr和C的stdin、stdout、stderr都是同步的,即iostream 对象和 and cstdio流是同步的,同步关系如下:

C stream iostream object
stdin cin
win
stdout cout
wout
stderr cerr
wcerr
clog
wclog
1
2
3
4
5
6
std::ios_base::sync_with_stdio(false);
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
printf("2 ");
}

windows下输出是:2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
linux下是:1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2
正是因为这种同步,所以cin、cout比scanf、printf速度要慢,如果我们在使用cin、cout输入输出前加一句’‘‘std::ios_base::sync_with_stdio(false)’’',其实速度和scanf、printf差不多。

1
2
3
4
5
6
7
8
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
}
while(1);
//在windows下立即输出10个1
//linux下不输出
//说明windows下默认cout是无缓冲的,linux下是有缓冲的,cout换成printf也是一样。

只有缓冲区被刷新的时候缓冲区中的内容才会写入真实的文件或输出设备上。这五种情况下会刷新输出缓冲区:
1.程序正常结束。作为main返回工作的一部分,将清空所有的输出缓冲区。
2.在一些不确定的时候,缓冲区可能已经满了,在这种情况下,缓冲区将会在写下一个值之前刷新。
3.用操纵符显示地刷新缓冲区,如用endl。
4.在每次输出操作执行完毕后,用unitbuf操纵符设置流的内部状态,从而清空缓冲区。
5.可将输出流与输入流关联起来,在读输入流时将刷新其关联的输出缓冲区。

我们可以通过函数 setbuf 和 setvbuf 自己设置输入输出流的缓冲区,需要注意的是不管程序中申请的的缓冲区实际大小为多少,setbuf都将缓冲区设置的大小为BUFSIZ,本人也在某本书上看到一个sample。
args = emalloc(BUFSIZ);
这是stdio.h 中的一个宏定义,内容如下:

1
2
3
4
/* Default buffer size. */
#ifndef BUFSIZ
define BUFSIZ _IO_BUFSIZ
#endif

BUFSIZ有一个_IO_BUFSIZ大小,这个_IO_BUFSIZ又是多大呢?

1
#define _IO_BUFSIZ _G_BUFSIZ

来自libio.h头文件,那么接着……

1
#define _G_BUFSIZ 8192

来自_G_config.h,最终确认BUFSIZ大小是8192,还有更快的方法就是cout这个BUFSIZ,直接出现大小。(以上三段代码在fedora25环境下测试)

setvbuf则可以设置缓冲区大小以及缓冲区的模式(行缓冲、全缓冲、无缓冲),需要注意的是这两个函数设置的是c的输入输出缓冲区,因为C和C的缓冲区是同步的,所有该函数会对C有影响

1
2
3
4
5
6
7
8
9
char buf[1024];
setbuf(stdout, buf);
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
}
while(1);//此时windows下输出了10个1,linux下没有任何输出。
//因为windows是通过cmd窗口设定缓冲区
//更改对应注册表键值或者设置即可

因为默认情况下,cin是和cout绑定的,cin 会刷新cout的缓冲区,可以用函数cin.tie(0)来解绑定。所以在上面代码的基础上,在while(1); 前面加上:int a; cin>>a; 此时所有的1就可以输出了。

1
2
3
4
5
6
7
8
char buf[1024];
setbuf(stdout, buf);
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
}
int a; cin>>a;
while(1);//linux windows均输出1

加上cin.tie(0)后,以下的代码没有输出1,因为cin已经和cout解绑定了,cin刷新不了cout的缓冲区。(可以cin.tie(&cout)来绑定,注意cout没有tie方法)

1
2
3
4
5
6
7
8
9
char buf[1024];
setbuf(stdout, buf);
cin.tie(0);
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
}
int a; cin>>a;
while(1);//windows和linux下均没有输出。

有点奇怪的是以下代码还是会输出1,即默认缓冲区的情形下,解除绑定没有产生效果。SOF上有人这么解释。

1
2
3
4
5
6
7
cin.tie(0);
for(int i = 0 ; i < 10; i++)
{
cout<<"1 ";
}
int a; cin>>a;
while(1);