无论标准I/O操作文件还是终端,都有操作缓存区。只不过这个过程是看不到的,但这一细节是不可忽略的。在使用标准 I/O
函数时,一定要注意缓存区的存在,避免产生不必要的失误。
首先,借用一个示例,展示标准I/O函数操作时,产生的缓存区问题。
#include <stdio.h> #include <string.h> int main(int argc, const char *argv[]) { while (1) { sleep(1); printf("hello world"); } return 0; }点击复制复制失败已复制
上述程序的意思是每秒输出一次 hello world
,但是编译运行后的现象并不是我们所期待的,运行后程序一直处于停滞状态(和卡死一样),直到一段时间后,一次性显示输出数据。
如果想按照我们的想法输出,需要在标准输出函数 printf()
中添加换行符,如下所示:
printf("hello world\n");点击复制复制失败已复制
由此,可以很明显地看出缓存区的存在。在没有换行符时, printf()
函数并没有立即将字符串输出到终端,是因为其字符串被输出到了缓存区中。经历一段时间之后,数据一次性输出,说明缓存区满足了刷新的条件(缓存区被写满了),此时执行系统调用,将缓存区中的数据一次性输出到终端上。添加换行符,则成功解决了这一问题,说明换行符不仅有换行的作用,同时也具有刷新缓存区的功能。
经过代码的论证,可以很明显看到标准I/O函数在使用操作时的缓存区问题,接下来则需要讨论缓存区的类型,以及缓存区的刷新条件。
标准I/O提供了三种类型的缓存,即全缓存、行缓存和不缓存。
全缓存
当流与文件相关联时,所操作的缓存区为全缓存。通俗的讲,即当使用标准I/O函数操作流指针,而该流指针是与文件有关联的流指针时,访问的缓存区为全缓存区。
例如, FILE *fp = fopen()
,标准函数操作 fp
。
行缓存
当流与终端相关联时,所操作的缓存区为行缓存。通俗的讲,当使用标准I/O函数操作流指针,而该流指针是与终端有关联的流指针时,访问的缓存区为行缓存区。
例如,标准I/O函数操作 stdin
、 stdout
。
不缓存(没有缓存区)
当使用标准I/O函数操作的流指针是不带缓存区的流指针时,没有缓存区。
例如,标准I/O函数操作 stderr
。
提示
对于系统预定义的两个流指针 stdout
和 stderr
,二者均被用来实现向终端输出信息,二者不同的地方就在于缓存区的问题。 stderr
通常被用来在特定的情况下,向终端输出错误警示信息使用,因此当程序出现问题时,必然需要立刻将错误警告输出,以便及时对程序修改,此时不需要缓存区的存在。如果错误信息保存在缓存区,则无法及时得到警告,造成不可预计的后果。