C语言标准输入输出缓冲区

Posted by persuez on July 7, 2018

注: 以下实验环境为Ubuntu 18.04LTS(只有64bit),gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0

缓冲区例子

我们先来说说什么是C语言中的缓冲,直接来看一个例子体会一下就知道了。我们以printf函数和stderr为例,先说明stdout(对应printf)是遇到换行符或缓冲区满之后或程序结束后才输出缓冲,stderr一般是无缓冲的:

/*
** 在我的实验环境中,缓冲区大小默认为1024
*/
#include <stdio.h>

int main()
{
  while(1) {
    printf(".");
    fprintf("1");
  }
  
  return 0;
}

运行结果 (你可以数数,有1024个1,然后1024个’.’,…) 或者你可以通过用./a.out > t 2>&1重定向标准输出和错误到文件t中,这样就可以很容易看到运行结果(因为程序跑的很快,很难找到开始运行出来的那一行),这时的缓冲区大小是4096个字节。

缓冲区解释

C语言的标准输入输出是分缓冲和无缓冲的。这里面缓冲分为全缓冲和行缓冲。

  • 全缓冲: 全缓冲是指当缓冲区满了才进行 I/O 的读写操作。一般磁盘文件是全缓冲的(缓冲区一般为4096个字节)。
  • 行缓冲: 行缓冲是指遇到换行符(即’\n’)后进行 I/O 操作,当然缓冲区满了也要进行操作。注意:换行符也被读入缓冲区。(缓冲区一般为1024个字节)

一般地,标准输入stdin和标准输出stdout为行缓冲,标准错误输出stderr无缓冲。但是ANSI C要求下列缓存特征(参考APUE):

  • 当且仅当标准输入和标准输出并不涉及交互作用设备时,它们才是全缓存的。
  • 标准出错决不会是全缓存的。

但是,很多系统是这么实现的:

  • 涉及到交互时,标准输入输出为行缓冲,否则为全缓冲
  • 标准错误输出总是无缓冲

Unix中用宏_IO_LINE_BUF_IO_UNBUFFERED#include <stdio.h>即可)来判断流属于哪个缓冲类型,判断方法(以stdin举例)为:用stdin->_flags_IO_LINE_BUF_IO_UNBUFFERED进行按为与运算,如果运算结果不为0,则为相应的缓冲类型;如果二者都为0,那么就是全缓冲。

下面验证之前的说明,即判断标准输入输出和标准错误输出的缓冲类型(以stdin为例子,stdoutstderr也同理,有兴趣的同学自己验证):

/*
** 没有交互时,stdin的缓冲类型
*/
#include <stdio.h>

int main()
{
  if (stdin->_flags & _IO_LINE_BUF) {
    printf("stdin is Line-buf.\n");
  } else if (stdin->_flags & _IO_UNBUFFERED) {
    printf("stdin is Non-buf.\n");
  } else {
    printf("stdin is full-buf.\n");
  }

  return 0;
}

运行结果

/*
** 有交互时,stdin的缓冲类型
*/
#include <stdio.h>

int main()
{
  int a;
  scanf("%d", &a);
  
  if (stdin->_flags & _IO_LINE_BUF) {
    printf("stdin is Line-buf.\n");
  } else if (stdin->_flags & _IO_UNBUFFERED) {
    printf("stdin is Non-buf.\n");
  } else {
    printf("stdin is full-buf.\n");
  }

  return 0;
}

运行结果