(本文章关于缓冲区概念的理解大部分取自:http://developer.51cto.com/art/201107/277186.htm)
快递的寄送过程:
源地址(商家的仓库)——中转地(快递公司的仓库)——目的地(买家)
我们从淘宝商家买衣服,商家通过快递公司把商品送到我们手里的这个过程可以形象的解释下缓存区和流的这个概念。
1, 淘宝商家不会亲自把商品给买家送过来的,因为这样效率太低了,商家会通过快递公司这个中转,然后快递公司再把东西送给买家。淘宝商家就是在键盘上打字,买家就是程序,程序需要读取从键盘上的输入的字,缓冲区就是快递公司的仓库。
2, 商品的几种位置状态:商家仓库,快递仓库,买家手中,还有一种状态就是在路上。输入输出的流就是指的在路上。
3, 快递收货员收到商品就放到自己的中转仓库中。但是快递公司肯定等仓库中的商品积累到一定程度才开始派送。行缓冲就是遇到换行符时就认为需要执行I/O操作了。
一,缓冲区的概念
缓冲区又可以称为缓存。计算机中的内存可以被认为是硬盘的缓存。当cpu读取文件、执行程序时,不会直接从硬盘中读取,而是先把文件缓存到内存中,然后再从内存中读取。
对于C++程序来说,当类似cin,getchar这样的对象或者函数读取输入时,不会直接直接读键盘上的输入,而是这样的一个过程:cin——输入缓冲区——键盘。我们从键盘上输入的字符先存到缓冲区里面,cin从缓冲区里面读取输入。对于输出来说,程序的结果不会直接显示到屏幕上,而是先存放到缓冲区,然后cout把内容从缓冲区输出到屏幕。cin和cout本质上都是对缓冲区中的内容进行操作。
如果没有缓冲区就会大大降低CPU的效率,因为cpu将不得不一直等待用户的输入,而不能执行其他的操作,人打字输入的速度再快,也比不上CPU的执行速度,人在输入两个字符之间的间隔时间,cpu完全可以去干别的事情。
缓冲区分为三种全缓冲、行缓冲和不带缓冲。
1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新指的是缓冲区的内容被清空刷新,这也就意味着刷新之前系统会对缓冲区内容进行I/O读写。下面4种情况会触发缓冲区的刷新:
缓冲区满时;
执行flush语句;
执行endl语句;
关闭文件。
C/C++程序里面的缓冲区指的是为标准输入与标准输出设置的缓冲区,如果我们不认为的设置的话,系统会自动的为标准输入与标准输入设置一个缓冲区,这个缓冲区的大小通常是4Kb的大小。
ANSI C要求下列缓存特征:
(1) 当且仅当标准输入和标准输出并不涉及交互作用设备(键盘,屏幕)时,它们才是全缓存的。 读写文件的时候就是全缓存。
(2)标准出错决不会是全缓存的。
(3)标准输入和输出涉及交互作用设备时,虽然没有明确规定是不带缓存的还是行缓存的,但一般系统默认它们是行缓存的。
因此我们经常要用的标准输入和输出,stdin、stdout和stderr的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。cin和cout都是从缓冲区读取
二、流的概念
流是一个过程,一个动态的概念。可以把流想象成水在水管中流动的过程,想象成商品快递运送的过程。Cin和cout就是执行流这个过程的人。
对于输入,cin负责把输入缓冲区中的内容传递给程序;
对于输出,cout负责把输出缓冲区中的内容传递给屏幕。
Cin和cout把缓冲区的数据变成流,然后搬运到相应的目的地。Cin和cout就是个搬运工,搬运的过程就是流。
三、代码案例
第一段代码:
int main()
{
string str;
int i=0;
while (cin >> str)
{
cout << str<<endl;
cout << ++i << endl;
}
return 0;
}
程序执行过程中输入:i love you
最终结果是:
i
1
love
2
you
3
执行过程中,程序并不会在每次输入一个空格时就打印一次,而在在完全输入一行字符串并摁下回车后,才会打印。原因就是在我们输入回车之前的一行字符串都只是存放到了为标准输入分配的缓冲区中,这是一个行缓冲区,在遇到换行符之前,缓冲区不会刷新也就不会触发I/O操作,cin也就没有在读取数据。输入回车后,cin开始执行I/O操作,读取缓冲区中的字符:首先读取i,然后遇到了空格,此次读取完成,执行循环。然后接着读取love,又遇到了空格,读取完成,执行循环。最后读取了you。
第二段代码:
int main()
{
char c;
//第一次调用getchar()函数
//程序执行时,您可以输入一串字符并按下回车键,按下回车键后该函数才返回
c = getchar();
//显示getchar()函数的返回值
cout << c << endl;
//暂停
system("PAUSE");
//循环多次调用getchar()函数
//将每次调用getchar()函数的返回值显示出来
//直到遇到回车符才结束
while ((c = getchar()) != '\n')
{
printf("%c", c);
}
//暂停
system("PAUSE");
return 0;
}
执行程序,输入:abcdefg,然后回车
程序结果如下:
a
bcdefg
第一次执行到getchar时,由于此时缓冲区里面没有任何内容,所以程序等待键盘的输入,输入abcdefg后,然后输入回车,触发了行缓冲的条件,执行I/O,getchar开始读取缓冲区的内容,由于此函数只读取一个字符,所以读完字符a后,读取结束,执行下面的语句,将a打印到屏幕。由于缓冲区中字符只被读取了1个字符a,剩余的bcdefg还在缓冲区中,因此执行到while中的getchar时,直接读取缓冲区中的内容,也就是依次读取bcdefg,回车符也是缓冲区中的一个字符,当读取完回车后,while循环结束。