前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C/CPP 的全缓冲、行缓冲和无缓冲

C/CPP 的全缓冲、行缓冲和无缓冲

作者头像
CtrlX
发布2022-11-14 15:04:00
1.2K0
发布2022-11-14 15:04:00
举报
文章被收录于专栏:C++核心编程

1.简介

详见CPrimerPlus P218

为什么要有缓冲区?

  1. 把若干个字符作为一个块进行传输比逐个发送这些字符节约时间。
  2. 其次是如果用户打错字符,可以直接通过键盘修正。

C/C++中,基于 I/O 流的操作最终会调用系统接口 read() 和 write() 完成 I/O 操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O接口的调用次数。

缓冲方式存在三种,分别是:

(1)全缓冲。输入或输出缓冲区被填满,会进行实际 I/O 操作。其他情况,如强制刷新、进程结束也会进行实际I/O操作。

对于读操作来说,当读入内容的字节数等于缓冲区大小或者文件已经到达结尾,或者强制刷新,会进行实际的 I/O 操作,将外存文件内容读入缓冲区;对于写操作来说,当缓冲区被填满或者强制刷新,会进行实际的 I/O 操作,缓冲区内容写到外存文件中。磁盘文件操作通常是全缓冲的。

(2)行缓冲。输入或输出缓冲区遇到换行符会进行实际 I/O 操作(键盘输入通常是行缓冲,所以在按下Enter键时才刷新缓冲区)。其他与全缓冲相同。

(3)无缓冲。没有缓冲区,数据会立即读入内存或者输出到外存文件和设备上。标准错误输出 stderr 是无缓冲的,这样能够保证错误信息及时反馈给用户,供用户排查错误。

三种缓冲类型的宏定义在头文件<stdio.h>。

缓冲类型 宏 全缓冲 _IOFBF 行缓冲 _IOLBF 无缓冲 _IONBF Linux 环境下,下面一段代码可以很好地体现全缓冲和行缓冲的区别。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

int glob=6; 
int main(int argc, char** argv) {
	int var;  
    pid_t pid;  
    printf("a write to stdout\n");  
    if(pid=fork()<0) {  
        printf("fork error");  
    } else {  
        if(pid==0) {  
            glob++;  
            var++;  
        } else {  
            sleep(2);  
        }  
    }  
    printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);  
    exit(0);  

编译成功后默认生成a.out,运行结果如下:

编译成功后默认生成a.out,运行结果如下:

代码语言:javascript
复制
./a.out 
a write to stdout
pid=4823,glob=7,var=4195873
pid=4824,glob=7,var=4195873

./a.out > temp.txt
cat temp.txt
a write to stdout
pid=4864,glob=7,var=4195873
a write to stdout
pid=4865,glob=7,var=4195873

可见 printf() 在输出到标准输出(显示器)时,是行缓冲,遇到换行符时会将缓冲区内容输出到显示器,并清空缓冲区。当使用重定向命令时,标准输出被重定向到磁盘文件,此时标准输出变成全缓冲,遇到换行符不输出,而是被拷贝至子进程中,在父子进程结束后,各有一份输出。

2.缓冲区的设置

(1)缓冲打开或关闭,可使用函数setbuf()或者setbuffer()。参数buf指向缓冲区,表示开启缓冲,通常是全缓冲。将buf参数设置为NULL,表示关闭缓冲。注意,setbuffer()是非C标准库函数,常见于Linux。

setbuf() 的缓冲区长度至少为 BUFSIZ(定义在 stdio.h),否则可能会出现缓冲区溢出。setbuffer() 可以指定缓冲区大小。

代码语言:javascript
复制
//@header:stdio.h
//@brief:设置指定的缓冲区或关闭缓冲
//@param:stream:文件指针;buffer:缓冲区地址
//@notice:使用默认缓冲大小BUFSIZ(在stdio.h中定义)
void setbuf ( FILE * stream, char * buffer );

//@notice:同setbuf,但可指定缓冲区大小
void setbuffer(FILE *stream, char *buf, size_t size);

将 buffer 指定为 NULL,关闭标准输出缓冲。

将 buffer 指定为 NULL,关闭标准输出缓冲。

代码语言:javascript
复制
setbuf(stdout,NULL)

指定新的缓冲区。

代码语言:javascript
复制
static char newBuffer[BUFSIZ];//至少是BUFSIZ(定义在stdio.h),否则存在缓冲溢出可能
setbuf(stdout,(char*)&newBuffer);

//或者指定缓冲区大小
static char newBuffer[512];
setbuffer(stdout,(char*)&newBuffer,512);

(2)更改缓冲模式,可使用函数setvbuf()。

代码语言:javascript
复制
//@header:stdio.h
//@brief:更改缓冲模式并设置缓冲区
//@param:stream:文件指针;buf缓冲区地址;type:缓冲区模式;size:缓冲区大小
//@ret:0成功,非0失败
int setvbuf(FILE *stream, char *buf, int type, unsigned size);

例如,将流缓冲区设置为行缓冲,调用setvbuf()时,缓冲区地址设为NULL,缓冲区大小设为0。注意,前提是存在缓冲区。

代码语言:javascript
复制
setvbuf(stream,NULL,_IOLBF,0);	//将缓冲改为行缓冲

// 上面的代码等价于
setlinebuf(stream);   			//for Linux

如果调用setvbuf指定了缓冲区大小size大于0,缓冲区buf为NULL,则交由setvbuf进行malloc申请缓冲区。

代码语言:javascript
复制
// 间接申请1024字节全缓冲区
setvbuf(stream,NULL,_IOFBF,1024);

[1] C++ Reference.setvbuf() [2] C++ Reference.setbuf() [3] APUE.程序 8-1

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.简介
  • 2.缓冲区的设置
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档