标准I/O实现了三种类型的用户缓冲,并为开发者提供了接口,可以控制缓冲区类型和大小。
- 无缓冲(Unbuffered)
不执行用户缓冲。数据直接提交给内核。因为这种无缓冲模式不支持用户缓冲(用户缓冲一般会带来很多好处),通常很少使用,只有一个例外:标准错误默认是采用无缓冲模式。
- 行缓冲(Line-buffered)
缓冲是以行为单位执行。每遇到换行符,缓冲区就会被提交给内核。行缓冲对把流输出到屏幕时很有用,因为输出到屏幕的消息也是通过换行符分隔的。因此,行缓冲是终端的默认缓冲模式,比如标准输出。
- 块缓冲(Block-buffered) 或 完全缓冲(full buffered)
缓冲以块为单位执行,每个块是固定的字节数。很适用与处理文件。默认情况下,和文件相关的所有流都是块缓冲模式。
大部分情况下,默认的缓冲模式对于特定场景是最高效的。但是,标准 I/O 还是提供了一个接口,可以修改使用的缓冲模式:
#include <stdio.h> int setvbuf(FILE *stream, char *buf, int mode, size_t size);
mode 值必须是以下之一:
_IONBF : 无缓冲
_IOLBF : 行缓冲
_IOFBF : 块缓冲
在 _IONBF 无缓冲模式下,会忽略参数 buf 和 size;对于其他模式,buf 可以指向一个 size 字节大小的缓冲空间,标准 I/O 会用它来执行对给定流的缓冲。如果 buf 为空,glibc 会自动分配指定 size 的缓冲区。
setvbuf() 函数必须在打开流后,并在执行任何操作之前被调用。
行缓冲:
1 #include <stdio.h> 2 3 int main(int argc, char* argv[]) 4 { 5 printf("hello world"); 6 while(1); 7 return 0; 8 }
执行程序,看到没有输出“hello world”
如果在“hello world”后面加上“ ”,可以看到输出了“hello world”
说明标准输出的缺省模式是行缓冲
行缓冲大小:
1 #include <stdio.h> 2 3 int main(int argc, char* argv[]) 4 { 5 int i = 0; 6 while(i < 1024) 7 { 8 printf("h"); 9 i++; 10 } 11 while(1); 12 return 0; 13 }
如果把1024改成1025,就能看到输出,说明行缓冲的大小是1024字节
无缓冲:
1 #include <stdio.h> 2 3 int main(int argc, char* argv[]) 4 { 5 fprintf(stderr, "hello world"); 6 while(1); 7 return 0; 8 }
可以看到“hello world”输出了,表明stderr是无缓冲的
1 #include <stdio.h> 2 3 int main(int argc, char* argv[]) 4 { 5 setvbuf(stdout, NULL, _IONBF, 0); 6 fprintf(stdout, "hello world"); 7 while(1); 8 return 0; 9 }
可以把 stdout 设置为无缓冲
1 #include <stdio.h> 2 3 char buf[2046]; 4 5 int main(int argc, char* argv[]) 6 { 7 setvbuf(stdout, buf, _IOLBF, 2046); 8 int i = 0; 9 while(i < 2047) 10 { 11 printf("h"); 12 i++; 13 } 14 while(1); 15 return 0; 16 }
可以改变缓冲区大小
行缓冲的输出条件:遇到换行符“ ”;缓冲区满
1 #include <stdio.h> 2 3 char buf[2046]; 4 5 int main(int argc, char* argv[]) 6 { 7 setvbuf(stdout, buf, _IOLBF, 2046); 8 int i = 0; 9 while(i < 2046) 10 { 11 printf("h"); 12 if (i == 2000) printf(" "); 13 i++; 14 } 15 while(1); 16 return 0; 17 }
在关闭流时,其使用的缓冲区必须存在。一个常见的错误就是把缓冲区定义成某一个作用域中的局部变量,在关闭流之前就退出这个作用域了。
1 #include <stdio.h> 2 3 int main(int argc, char* argv[]) 4 { 5 char buf[BUFSIZ]; 6 setvbuf(stdout, buf, _IOFBF, BUFSIZ); 7 printf("Arrr! "); 8 return 0; 9 /* 'buf' exits scope and is freed, but stdout isn't closed until later */ 10 }
这类错误可以通过两种方式解决:一是在离开作用域之前显示关闭流,二是把 buf 设置为全局变量
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #define BUF_SIZE 10 5 char buf[BUF_SIZE]; 6 7 int myprintf(char* str) 8 { 9 if (NULL == str) return; 10 char *cur = str; 11 int len = 0; 12 int have_n = 0; 13 while(*cur != '