缓冲区
之前介绍了IO,当我们新建一个file结构体的时候来看看会怎么加入链表
首先我们建立一个1.txt文件,里面随便写入内容echo "liyou">>1.txt
然后编译生成可执行程序,源代码如下:
#include<stdio.h>
intmain()
{
FILE *fp;
charbuff[255];
chara[]="12345";
fp = fopen("./1.txt", "r");
fscanf(fp, "%s", buff);
printf("1 : %sn", buff );
fgets(buff, 255, (FILE*)fp);
printf("2: %sn", buff );
fclose(fp);
}
这里面使用到了FILE结构体指针,使用fopen打开文件,这时候就会触发init初始化操作,从而进入_IO_list_all链表
这里加入链表的方式是在_IO_list_all之后加入(_IO_2_1_stderr_之前)
并且使用open函数的话建立的链表是在堆地址上开辟的,也就是说open一定会调用内存分配的函数,会触发__malloc_hook,当fclose之后会释放内存
也就是说一定会触发__free_hook
并且触发解链_IO_list_all之后fp指针如果不手动清空的话会出现fp和_IO_list_all都指向同一块内存位置(UAF)
首先什么是缓冲区,为什么需要缓冲区
在计算机里缓存是一个很重要的概念,C标准库里大量使用了缓存,最为典型的就是标准输入和标准输出的缓存,利用好缓存可以大幅提高程序性能。
我们来看看菜鸟教程里面的描述:
C语言setbuf()函数:把缓冲区与流相关联
头文件:
1
|
#include <stdio.h> |
函数setbuf()用于将指定缓冲区与特定的文件流相关联,实现操作缓冲区时直接操作文件流的功能。其原型如下:
1
|
void setbuf ( FILE * stream, char * buf); |
【参数】stream为文件流指针,buf为缓冲区的起始地址。
如果参数buf 为NULL 指针,则为无缓冲,setbuf()相当于调用setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZE)。
说明:在打开文件流后,读取内容之前,可以调用setbuf()来设置文件流的缓冲区(而且必须是这样)。
C语言setvbuf()函数
描述
C 库函数 int setvbuf(FILE *stream, char *buffer, int mode, size_t size) 定义流 stream 应如何缓冲。
声明
下面是 setvbuf() 函数的声明。
int setvbuf(FILE *stream,char*buffer,int mode,size_t size)
参数
-
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
-
buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
-
mode -- 这指定了文件缓冲的模式:
模式 | 描述 |
---|---|
_IOFBF | 全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。 |
_IOLBF | 行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。 |
_IONBF | 无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略。 |
-
size --这是缓冲的大小,以字节为单位。
返回值
如果成功,则该函数返回 0,否则返回非零值。
我们来看一个例子:
#include<stdio.h>
#include<unistd.h>
intmain()
{printf("liyou66666");
while(1){
sleep(1);
}
return0;}
在Linux中包含unistd.h头文件才能使用sleep函数
运行后发现
输出的结果是空的,程序一致在死循环,为什么没有输出结果呢?正常情况在进入while的时候就已经执行了printf,这里为什么没有输出呢?这就涉及到缓冲区的问题了
printf函数默认是行缓冲,当输出字符串里有n或者行缓冲区被填满或者手动调用fflush函数才会一次性将数据输出。
所以这里我们想输出内容的话有两种办法,第一就是执行
fflush(stdout); //强制刷新标准输出缓冲区
第二就是
printf("n"); //换行,默认标准输出会立即输出刷新缓冲区
这个stdout是什么呢?
在stdout.c中我们看到stdout和stderr定义如下:
FILE *stdout = (FILE *) &_IO_2_1_stdout_;
stdout和文件描述符1绑定,需要注意的是标准错误输出是无缓冲模式,写入什么数据就立即输出什么数据
缓冲区的作用
在计算机里应用程序调用一个系统调用从用户态进入内核态再将结果回到用户态开销较大。如果我们调用printf函数,每次输出一个字符都要从用户态切换到内核态,那么连续输出多个字符开销成本将会非常大,这个时候缓存就起到非常大的作用了,输出的字符串先在应用程序里缓存起来,缓存到一定数量后再调用系统调用一次性将缓存数据输出到标准输出。由于只调用了一次系统调用,比连续调用多个系统调用性能高上不少。
原文始发于微信公众号(由由学习吧):缓冲区
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论