文件缓冲区
提到文件缓冲区这个概念我们好像并不陌生,但是我们对于这个概念好像又是模糊的存在脑海中,之间我们在介绍c语言文件操作已经简单的提过这个概念,今天我们不妨深入理解什么是文件缓冲区。
为什么需要文件缓冲区
当我们在程序中写下一条printf语句时,我们希望将这条语句的内容打印到屏幕上。但是如果你将语句放在循环中,难道你执行一次循环那么操作系统就要打印一次这条数据么?答案当然不是
我们对于程序数据和磁盘数据的交换的时候不是直接进行交换的,他们之间存在了一个数据缓冲区,只有缓冲区中的数据达到某种条件时,相应的对象才会对缓冲区中的数据进行操作
证明存在输入缓冲区
下面这段代码很多朋友可能不知道,当你输入一串字符串,假设输入abcdef时,一个回车,你以为接下来要确定输入Y/N时这个代码实际直接结束了,他跳过了输入这个字符的环节(感兴趣的同学试一试)
int main()//验证输入缓冲区
{
int ch;
char arr[10] = { 0 };
printf("请输入密码\n");
scanf("%s", arr);
printf("请确认密码(Y/N)\n");
ch = getchar();
if (ch == 'Y')
{
printf("y");
}
else
{
printf("n");
}
system("pause");
return 0;
}
为什么呢?让我来告诉你
那我们有没有解决这个bug的方案呢?有,在vs2008下我们可以使用fflush函数来清空输入缓冲区,但是这个函数在高级编译器(vs2013)以后就禁止使用了。但我们可以自己清空缓冲区
while (getchar() != '\n')//在getchar()之前获取完缓冲区中所有的字符
{
;
}
证明存在输出缓冲区
此代码必须在Linux下验证
如下代码很多同学以为会每秒输出一个a,但是事实上他输出一个后就不在输出了,这其实也就证明输出缓冲区是存在的,只有当缓冲区的数据满时,他才会一次性打印在屏幕上。
int main()
{
while (1)
{
printf("a");
Sleep(1);//linux下停止1s
}
return 0;
}
数据缓冲的方式
我们现在已经证明了确实是有数据缓冲区的存在的,那么数据的缓冲方式到底有哪些呢?
- 无缓冲:无缓冲的意思是说,直接对数据进行操作,无需经过数据缓冲区缓存,系统调用接口采用此方式
- 行缓存:缓冲区的数据每满一行即对数据进行操作,而通常情况下向屏幕打印数据就是行缓存方式
- 全缓冲:缓冲区的数据满时才对数据进行操作,通常向文件中写数据使用的就是全缓冲方式
现在我们来证明这几种数据的缓冲方式:
分别使用printf和write向屏幕打印数据。
想屏幕打印数据:没问题
重定向到文件中:也拿到了我们想要的结果
现在来看看如何证明缓冲区的三种方式
修改代码,给函数的最末尾加了一句fork
打印到屏幕:
重定向到文件:奇怪的现象出现了,文件里居然有两个hello printf
现在我们来说说这两种不同的现象吧:
- 打印到屏幕:因为write是系统调用函数,无缓冲打印数据,printf是行缓冲打印数据,所以将数据打印到屏幕上再调用fork(其实就是fork没起到作用)是我们预料到的
- 重定向到文件:为什么文件中出现了两次hello printf?首先我们要知道向文件写数据是全缓冲,但是write同样是无缓冲,所以直接写入到文件中,printf打印的数据先被放到了缓冲区中,此时我们调用了子进程,子进程对父进程的数据进行了写时拷贝,对缓冲区中的数据多操作了一次,所以我们发现此时文件中的hello printf就有了两份
缓冲区的本质
经过上面的例子,我们实际上还要明白一点,实际上缓冲区这个概念是c语言提供给我们的,我们所调用的系统调用函数其实并不存在缓冲区,缓冲区就是本质上就是一段内存,至于怎么找到这段内存,也就是找到缓冲区,还记得我们描述文件的file结构体么?
也就是说,通过file结构体,我们也就能找到文件相对应的缓冲区了。