C++输入输出流详细理解

今天重读C++ Primer时,重新回顾了一下C++中iostream标准库,标准库提供了四个标准输入输出流,包括输入流cin和输出流cout,cerr,clogcerr通常用来输出警告和错误信息给程序的使用者,而clog对象用于产生程序执行的一般信息。

定义

1
2
3
4
extern istream cin; /// Linked to standard input
extern ostream cout; /// Linked to standard output
extern ostream cerr; /// Linked to standard error (unbuffered)
extern ostream clog; /// Linked to standard error (buffered)

标准输入流

iostream标准库中标准输入流中只有cin一个,指的是从输入设备(如键盘)中向程序输入数据,程序通过cin从标准输入流中获取数据。

1
2
3
4
5
6
7
8
#include <iostream>
#include <string>
int main()
{
std::string s;
std::cin >> s;
std::cout << s << std::endl;
}

标准输出流

iostream标准库中提供的输出流有cout,cerr,以及clog三个。

cout,cerr与clog区别

这三个输出流到底有什么区别呢?根据GNU官方的解释,其区别如下:

  • cout : 输出数据经过缓冲区(buffered[标准输出流的缓冲区]),可被重定向
  • cerr: 输出数据不经过缓冲区(unbuffered),不可被重定向
  • clog:输出数据经过缓冲区(buffered[标准错误流的缓冲区]),不可被重定向

参考链接:

https://gcc.gnu.org/onlinedocs/gcc-4.6.0/libstdc++/api/a00914_source.html

https://gcc.gnu.org/onlinedocs/gcc-4.6.0/libstdc++/api/a01140.html#a7e2a2fc4b5924b7292c0566ca4c95463

实验程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
// sleep 10s后输出123
int main()
{
cout << "123";
sleep(10);
}
// sleep前输出123
int main()
{
cerr << "123";
sleep(10);
}
// sleep前输出123
int main()
{
clog << "123";
sleep(10);
}

​ 从上面的实验结果可以看出,cout与cerr的行为与上述cout和cerr的区别一致,但是从现象看clog并没有buffered,这是什么原因呢?

​ 产生上述现象的原因从上面三个输出流的定义可以看出,cout是使用的标准输入(stdout)的缓冲区,clog是使用的标准错误流(stderr)的缓冲区,由于stderr的缓冲区大小默认为0,所以虽然clog输出流有缓冲,但是缓冲区大小为0,所以上述效果与无缓冲一致。

​ 可通过设置标准错误流的缓冲区来达到clog缓冲的效果。

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <stdio.h>
#include <unistd.h>
char buffer[256];
int main()
{
setbuf(stderr, buffer);
std::clog << "123";
sleep(10);
}

endl与\n区别

​ 在使用endl与\n的作用相同,但是还是有一些区别的。

  • 对于无缓冲的输出流如cerr,endl与\n完全一致。
  • 对于带缓冲的输出流cout和clog,\n仅仅输出换行,而endl除了输出换行之外,还刷新输出流或错误流缓冲区。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <unistd.h>
// 直接打印到屏幕上:在sleep前输出123
// 重定向到 文件中: 在sleep前输出123
int main()
{
std::cout << "123" << std::endl;
sleep(3);
}
// 直接打印到屏幕上:在sleep前输出123
// 重定向到文件中 : 在sleep后输出123
int main()
{
std::cout << "123\n";
sleep(3);
}

既然\n不刷新缓冲区,那为什么第二个程序在输出到terminal的时候会在sleep前打印123呢?这是由于当打印到屏幕上的时候,输出流的缓冲为行缓冲,当重定向到文件中时候,输出流缓冲为全缓冲

行缓冲与全缓冲

从上面可以看出标准输出在输出到屏幕和输出到文件中默认的缓冲类型不一致,当输出到屏幕的时候,为行缓冲;当输出到文件的时候为全缓冲。

  • 全缓冲:填满标准I/O缓冲区才进行实际的I/O操作。
  • 行缓冲:当缓冲区内容遇到换行时,即进行实际的I/O操作。

所以当cout打印到屏幕上的时候,使用cout<<”\n”,也会立即显示结果。