1.什么是缓冲区?
缓冲区又被称为缓存,是内存空间的一部分,也就是说在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分存储空间就叫做缓冲区。
缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
2.为什么要引入缓冲区?
比如我们要从磁盘中读数据,我们先把读出的数据放到缓冲区中,计算机再从缓冲区中取出数据,等缓冲区中的数据取完后,再从磁盘中读取数据,这样可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,所以应用缓冲区可以大大提高读写速度。
又比如使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印 ,这时我们的cpu可以处理别的事情。
缓冲区就是一块内存空间,以scanf()和printf()为例,它用在输入输出设备和cpu之间,用来缓存数据。它使得低俗的输入输出设备和高速的cpu能够协调工作,避免低俗的输入输出设备占用cpu。
输入设备->输入缓冲区->程序 程序->输出缓冲区->输出设备
3.缓冲区的类型
全缓冲:当填满标准I/O缓存后才进行实际的I/O操作,全缓冲的典型代表是对磁盘文件的读写。
行缓冲:当在输入和输出中遇到换行符时或满一行(1024个字节)才会刷新,即把数据从缓冲区中取出来。
不带缓冲:不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
4.刷新的条件
标准输入输出是行缓冲,行缓冲刷新需要满足的条件有:
满刷新,即一行满了(1024个字节)才刷新
遇到 '\n'会刷新
调用fflush()函数
程序结束
下面我们说一下scanf()函数使用时的一些问题
#include <stdio.h>
int main()
{
int a = 1, b = 2, c = 3, d = 4;
scanf("%d", &a);
scanf("%d", &b);
printf("a=%d, b=%d\n", a, b);
scanf("%d %d", &c, &d);
printf("c=%d, d=%d\n", c, d);
return 0;
}

这里我们看到前两个数据被正确读取了,剩下一个a10,而第三个scanf()函数要求输入两个十进制整型,而a10无论如何也不符合要求,所以从结果中,我们看到读取失败了。
由此可见,scanf()在遇到不符合要求的数据时,会读取失败,不会跳过不符合要求的数据,继续等待用户输入。
下面我们把代码稍作修改
#include <stdio.h>
int main()
{
int a = 1, b = 2, c = 3, d = 4;
scanf("%d", &a);
scanf("%d", &b);
printf("a=%d, b=%d\n", a, b);
scanf("%d", &c);
scanf("d=%d", &d);
printf("c=%d, d=%d\n", c, d);
return 0;
}

这里我们可以看出第三个scanf()函数遇到d=40时读取失败,而第四个scanf()函数把d=40读走了,这说明scancf()函数在遇到不符合输入格式的数据时,会读取失败,但不符合输入的数据依然保留在缓冲区中。
下面我们再来分析一段代码
#include <stdio.h>
int main()
{
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}

这里我们只是输入了一个a=10,第一个scanf()函数正确读走了数据,但是第二个scanf()函数还没有开始读取数据,没有等待用户输入数据,程序就结束了。
这是因为我们在输入a=10后还敲了一次回车键,也就是给缓冲区键入了一个换行符 '\n' ,而字符 '\n'不符合b=%d这样的输入格式,造成第二个scanf()函数读取失败。
如果我们换种输入方式呢?

这次我们就读取正确了,这里注意a=10和b=20之间是没有空格的,那如果有空格呢,结果会如何呢?

这里我们看到第二个scanf()函数又读取失败了,这是因为在缓冲区中,a=10被读走后,接下来是一个空格字符,而空格字符显然不符合我的b=%d这样的输入格式,所以第二个scanf()函数读取失败。
总结一下:
1. scanf()函数在以%d形式读取数据时,会跳过空格,换行,Tab这些空白字符,遇到形如a10这样不符合要求的数据时,会读取失败;
2. scanf()函数在以a=%d形式读取数据时,不会跳过空格,换行,Tab这些空白字符,在遇到这些空白字符或其他不符合要求的数据时会读取失败。
3. 如果数据不符合输入格式,则scanf()函数会读取失败,不再寻找输入缓冲区中后面的数据,不再等待用户输入新的数据,(即该scanf()函数调用结束),但不符合格式的数据依然保留在缓冲区中。
4.scanf()函数在以%c形式读取数据时不会跳过字符
5.scanf()函数在以%s形式读取数据时遇到空格终止读取,若要读取一行,可用gets()函数。