iostream 缓冲区详解(cin和cout)
缓冲区
缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。
缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
缓冲区的类型
缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新
下列情况会引发缓冲区的刷新:
1、缓冲区满时
2、执行flush语句
3、执行endl语句
4、关闭文件
cin
和cout
cin
和cout
是C++中作为输入和输出的标准 cin
是C++编程语言中的标准输入流对象,即istream
类的对象,cin
一般使用的是行缓冲机制,当输入接收到回车的时候就会进行数据的刷新,cin
主要用于从标准输入读取数据,这里的标准输入,指的是终端的键盘 cout
是流的对象,即ostream
类的对象,这里的标准输出指的是命令行窗口
标准输入缓冲区
当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键会被转换为一个换行符
\n
,这个换行符\n
也会被存储在cin
的缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7
,而不是6。
cin
读取数据也是从缓冲区中获取数据,缓冲区为空时,cin
的成员函数会阻塞等待数据的到来,一旦缓冲区中有数据,就触发cin
的成员函数去读取数据。
缓冲区和刷新
总所周知,要了解一个机制,最好的方法就是用一个实际的例子来进行说明
观察如下代码:
int a;
while (cin >> a) {
cout << a <<endl;
}
对于如上的代码,当我们输入:1 2 3 4 5
的时候,我们想要的也许是,输入一个处理一个
但是实际上在我们输入五个数字之后并敲下回车键,cout
才一并将所有的五个数输出
在我们不了解的时候,我们也许会疑惑,就算是一次输出五个,那也应该是只进入一次循环吧,为什么在我们调用单步调试的时候,其实际上却进入了五次循环,a
的值到底在哪里存储?cin
的>>
又在何时执行的?
要了解这些,我们首先应该知道,cin
的>>
运算符重载函数是阻塞式的,所以我们在输入:1 2 3 4 5
的时候,while(cin>>a)
这个语句其实还没执行完
在我们输入换行符之前,我们实际上并没有对a
进行任何的赋值,我们只是在缓冲区中构建了这样的一个数据结构(我的猜想):
在输入回车符之后,我们执行真正的>>
运算符函数操作,如下:
- 根据这个数据结构将数据对应的变量进行赋值(赋值之前判断型别是否能够对应,不能的话,就将状态置为
false
)- 赋值完毕,返回
istream
的引用- 然后根据
istream
的operator bool()
函数,返回流的状态是否正常,如果正常,返回true
,然后进入循环。
输出a
之后,再次进入刚才的>>
函数中,由于缓冲区中还有数据没有读出,所以我们接着根据那个数据结构,一个个的将数据读出进a
,并进行while
的判断然后进入循环
读取完毕之后,cin
的>>
继续进入阻塞状态等待用户的输入
这就是一个完整的cin
的输入流程
如何清空缓存区
从上文中可以想到,上一次的输入操作可能会导致缓冲区中的数据堆积,影响下一次的输入
,比如如下代码:
int main()
{
int a;
int i = 1;
while (cin >> a) {
cout << a <<endl;
i++;
if (i == 5)break;
}
char ch;
cin >> ch;
cout << "ch: " << ch;
system("pause");
return 0;
}
当我们输入1 2 3 4 5
的时候,输出为:
1
2
3
4
ch: 5
可以看到,由于缓存区有残余数据,导致了其后ch
的cin>>
操作出现了错误,并且由于输入缓冲区未读取完,导致cin
的条件状态也出现了错误。
所以我们应该使用clear()
进行条件状态的复位,然后清空输入缓冲区:
函数原型:istream &ignore( streamsize num=1, int delim=EOF );
函数作用:跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符)。
如下为示例:
int main()
{
int a;
int i = 1;
while (cin >> a) {
cout << a <<endl;
i++;
if (i == 5)break;
}
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
char ch;
cin >> ch;
cout << "ch: " << ch;
system("pause");
return 0;
}
当我们再次输入:1 2 3 4 5
,输出如下:
1
2
3
4
c
ch: c
可以看到,缓冲区的剩余数据被忽略掉了