setbuf与setvbuf函数,C语言setbuf与setvbuf函数详解
在讨论 setvbuf 与 setbuf 函数之前,先来看如下一段示例代码:
因此,运行上面的示例程序,然后实时查看 testfprintf.log 文件与 testwrite.log 文件,会发现 testfprintf.log 文件不会被实时写入,只有当写入的数据的大小为 4096 字节的倍数的时候才会被写入;而 write 函数则不同,因为它不进行任何缓冲(直接写入磁盘),所以文件 testwrite.log 不断有数据写入,运行结果如图 1 所示。
图 1 示例代码的运行结果
在上面的示例中不难发现,通过提供缓冲区可以尽可能减少 read 和 write 调用的次数,从而降低执行 I/O 的时间。在 C 语言中,标准 I/O 库提供了 3 种类型的缓冲。
在默认情况下,全缓冲的缓冲区可以由标准 I/O 例程自动刷新。当然,也可以通过调用 fflush 函数来强制刷新一个数据流。但需要特别注意的是,在标准 I/O 库方面,flush 函数意味着将缓冲区中的内容写到磁盘上;而在终端驱动程序方面,flush 函数则表示丢弃已存储在缓冲区中的数据。
很显然,它允许我们一次输出一个字符(如 fputc 函数),但只有在写完一行之后才进行实际 I/O 操作。当流涉及一个终端时,通常使用行缓冲。例如,使用最频繁的 printf 函数就是采用行缓冲,所以感觉不出缓冲的存在。
相对于这些系统默认的情况,也可以通过调用标准库函数 setbuf 和 setvbuf 来更改缓冲类型。函数 setbuf 和 setvbuf 将使得在打开文件后用户可以建立自己的文件缓冲区,而不使用由 fopen 函数打开文件所设定的默认缓冲区。函数 setbuf 和 setvbuf 的一般函数原型如下所示:
对于 setbuf 函数,当指定参数 buf 为 null 时,setbuf 函数将使得文件 I/O 不带缓冲。如下面的示例代码所示:
对 setvbuf 函数,则由 malloc 函数来分配缓冲区,参数 size 指明了缓冲区的长度(必须大于 0),而参数 mode 则表示缓冲的类型,取值如下所示:
int main(void) { FILE* fp=NULL; int fd; const char *f1="testfprintf.log"; const char *f2="testwrite.log"; fp = fopen(f1, "wb"); if(fp == NULL) { return -1; } fd = open(f2, O_WRONLY|O_CREAT|O_EXCL, 0666); if(fd < 0) { return -1; } while(1) { fprintf(fp, "fprintf------|\n"); write(fd, "write|\n", sizeof("write|\n")); sleep(1); } return 0; }在上面的示例代码中,使用 fprintf 函数对文件 testfprintf.log 执行写入操作,使用 write 函数对文件 testwrite.log 执行写入操作。这里需要注意的是,因为 fprintf 函数会缓冲 4096 字节的数据,只有当达到这么多字节的数据时才会进行实际的磁盘写入。
因此,运行上面的示例程序,然后实时查看 testfprintf.log 文件与 testwrite.log 文件,会发现 testfprintf.log 文件不会被实时写入,只有当写入的数据的大小为 4096 字节的倍数的时候才会被写入;而 write 函数则不同,因为它不进行任何缓冲(直接写入磁盘),所以文件 testwrite.log 不断有数据写入,运行结果如图 1 所示。
图 1 示例代码的运行结果
在上面的示例中不难发现,通过提供缓冲区可以尽可能减少 read 和 write 调用的次数,从而降低执行 I/O 的时间。在 C 语言中,标准 I/O 库提供了 3 种类型的缓冲。
1) 全缓冲
在进行 I/O 操作时,只有当 I/O 缓冲区被填满时,才进行实际的 I/O 操作。对于驻留在磁盘上的文件,通常就是由标准 I/O 库来实施全缓冲的。在一个流上执行第一次 I/O 操作时,相关标准 I/O 函数通常调用 malloc 来获得需要使用的缓冲区。在默认情况下,全缓冲的缓冲区可以由标准 I/O 例程自动刷新。当然,也可以通过调用 fflush 函数来强制刷新一个数据流。但需要特别注意的是,在标准 I/O 库方面,flush 函数意味着将缓冲区中的内容写到磁盘上;而在终端驱动程序方面,flush 函数则表示丢弃已存储在缓冲区中的数据。
2) 行缓冲
在这种情况下,只有当在输入和输出中遇到换行符时,才执行实际的 I/O 作。当然,因为标准 I/O 库用来收集每一行的缓冲区的长度是固定的,所以只要填满了缓冲区,即使还没有写一个换行符,也必须进行 I/O 操作。很显然,它允许我们一次输出一个字符(如 fputc 函数),但只有在写完一行之后才进行实际 I/O 操作。当流涉及一个终端时,通常使用行缓冲。例如,使用最频繁的 printf 函数就是采用行缓冲,所以感觉不出缓冲的存在。
3) 不带缓冲
标准 IO 库不对字符进行缓冲存储。在一般情况下,标准错误流 stderr 通常也是不带缓冲的。相对于这些系统默认的情况,也可以通过调用标准库函数 setbuf 和 setvbuf 来更改缓冲类型。函数 setbuf 和 setvbuf 将使得在打开文件后用户可以建立自己的文件缓冲区,而不使用由 fopen 函数打开文件所设定的默认缓冲区。函数 setbuf 和 setvbuf 的一般函数原型如下所示:
void setbuf(FILE *fp, char *buf);
int setvbuf(FILE *fp, char *buf, int mode, size_t size);
对于 setbuf 函数,当指定参数 buf 为 null 时,setbuf 函数将使得文件 I/O 不带缓冲。如下面的示例代码所示:
setbuf(fp, NULL);对 setvbuf 函数来说,由于 setbuf 函数没有返回值,因此也无法确定 setbuf 函数的调用是否成功。在实际使用中,应该尽量使用 setvbuf 来替换 setbuf 函数,以验证流被成功地更改。如下面的示例代码所示:
if (setvbuf(file, buf, buf ? _IOFBF : _IONBF, BUFSIZ) != 0) { }
对 setvbuf 函数,则由 malloc 函数来分配缓冲区,参数 size 指明了缓冲区的长度(必须大于 0),而参数 mode 则表示缓冲的类型,取值如下所示:
- _IOFBF,全缓冲。
- _IOLBF,行缓冲。
- _IONBF,不缓冲,此时忽略 buf 与 size 参数的值,直接读写文件,不再经过文件缓冲区缓冲。
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算