缓冲区又称为缓存, 它是内存空间的一部分. 也就是说, 在内存空间中预留了一定的存储空间, 这些存储空间用来缓冲输入或输出的数据, 这部分预留的空间就叫做缓冲区.
缓冲区根据其对应的是输入设备还是输出设备, 分为输入缓冲区和输出缓冲区.
1, 为什么要引入缓冲区
例如, 我们从磁盘里取信息, 我们先把读出的数据放在缓冲区, 计算机再直接从缓冲区中取数据, 等缓冲区的数据取完后再去磁盘中读取, 这样就可以减少磁盘的读写次数, 再加上计算机对缓冲区的操作大大快于对磁盘的操作, 故应用缓冲区可大大提高计算机的运行速度.
又比如, 我们使用打印机打印文档, 由于打印机的打印速度相对较慢, 我们先把文档输出到打印机相应的缓冲区, 打印机再自行逐步打印, 这时我们的 CPU 可以处理别的事情.
现在您基本明白了吧, 缓冲区就是一块内存区, 它用在输入输出设备和 CPU 之间, 用来缓存数据. 它使得低速的输入输出设备和高速的 CPU 能够协调工作, 避免低速的输入输出设备占用 CPU, 解放出 CPU, 使其能够高效率工作.
2, 缓冲区的类型
缓冲区分为三种类型: 全缓冲, 行缓冲和不带缓冲.
1) 全缓冲
在这种情况下, 当填满标准 I/O 缓存后才进行实际 I/O 操作. 全缓冲的典型代表是对磁盘文件的读写.
2) 行缓冲
在这种情况下, 当在输入和输出中遇到换行符时, 执行真正的 I/O 操作. 这时, 我们输入的字符先存放在缓冲区, 等按下回车键换行时才进行实际的 I/O 操作. 典型代表是标准输入 (stdin) 和标准输出(stdout).
3) 不带缓冲
也就是不进行缓冲, 标准出错情况 stderr 是典型代表, 这使得出错信息可以直接尽快地显示出来.
3, 缓冲区的大小
如果我们没有自己设置缓冲区的话, 系统会默认为标准输入输出设置一个缓冲区, 这个缓冲区的大小通常是 512 个字节的大小.
缓冲区大小由 stdio.h 头文件中的宏 BUFSIZ 定义, 如果希望查看它的大小, 包含头文件, 直接输出它的值即可: printf("%d", BUFSIZ);
缓冲区的大小是可以改变的, 也可以将文件关联到自定义的缓冲区, 详情可以查看 setvbuf()和 setbuf() 函数.
4, 缓冲区的刷新(清空)
下列情况会引发缓冲区的刷新: 缓冲区满时; 行缓冲区遇到回车时; 关闭文件; 使用特定函数刷新缓冲区.
5, 结合缓冲区谈谈 C 语言 getchar(),getche(),getch()的区别
先来看一下 getchar(), 其原型为: int getchar(void);
当程序调用 getchar()函数时, 程序就等着用户按键, 用户输入的字符被存放在键盘缓冲区中, 直到用户按回车为止 (回车字符也放在缓冲区中). 当用户键入回车之后, getchar() 函数才开始从键盘缓冲区中每次读入一个字符. 也就是说, 后续的 getchar()函数调用不会等待用户按键, 而直接读取缓冲区中的字符, 直到缓冲区中的字符读完后, 才重新等待用户按键. 打个比方, 键盘缓冲区就像是一条水管连着你的程序, 程序调用 getchar()函数用户输入字符就相当于往水管里注水, 这个水注多少取决于你输入多少, 当你按回车停止注水时, getchar()函数才会开始从键盘缓冲区, 也就是我们的水管里取水, 那每次只会读一个字符也就是每次取一定量的水, 当你在这之后继续调用 getchar()函数时, 会接着在水管里取上次没用完的水, 因为你的水管没清空 (缓冲区的刷新), 那这个阶段就不用你再输入了, 因为一调用 getchar() 函数就有水可取嘛, 直到水管里没水了, 你还调用 getchar()函数, 那这个时候你就得注水了也就是程序会等你按键.
通俗一点说, 当程序调用 getchar()函数时, 程序就等着用户按键, 并等用户按下回车键返回. 期间按下的字符存放在缓冲区, 第一个字符作为函数返回值. 继续调用 getchar()函数, 将不再等用户按键, 而是返回您刚才输入的第 2 个字符; 继续调用, 返回第 3 个字符, 直到缓冲区中的字符读完后, 才等待用户按键.
getchar()函数的执行就是采用了行缓冲. 第一次调用 getchar()函数, 会让程序使用者 (用户) 输入一行字符并直至按下回车键 函数才返回. 此时用户输入的字符和回车符都存放在行缓冲区. 再次调用 getchar()函数, 会逐步输出行缓冲区的内容.
请看下面一个例子:
运行结果如下:
再把程序做微小改变, 你再看看, 加深理解:
运行结果:
上面第二次打印时不是 2 而是空格, 你应该想到为什么了吧?
好, 我们再来看一个例子:
运行结果:
getchar()函数是从输入流缓冲区中读取数据的, 而不是从键盘 (终端) 缓冲区读取. 当读取遇到回车 (\n) 结束时, 这个'\n'会一起读入到输入流缓冲区的, 所以第一次接收输入时取走字符后会留下字符 \ n, 这样第二次 getchar()直接从缓冲区中把 \ n 取走了, 显然读取成功了, 所以不会再从终端读取! 其实这里的 10 恰好是回车符! 这就是为什么这个程序只执行了一次输入操作就结束的原因!
getch()和 getche()函数
在 TC2.0 时代, C 程序员总是喜欢在程序末尾加上 getch(), 来实现程序运行完了暂停不退出的效果. 如果不这样做, 在 TC2.0 的环境中 Ctrl+F9 编译并运行后会立即退出程序, 根本来不及看到结果. 这时如果要看结果, 就要按 Alt+F5 回到 DOS 环境中去, 很麻烦. 而如果在程序的结尾加上一行 getch(); 语句, 就可以省掉回 DOS 看结果这个步骤, 因为程序运行完了并不退出, 而是在程序最后把屏幕停住了, 按任意键才退出程序.
实际上, getch()的作用是从键盘接收一个字符, 且不带回显. 就是说, 你按了一个键后它并不在屏幕上显示你按的什么, 而继续运行后面的代码, 所以在 C 语言中可以用它来实现 "按任意键继续" 的效果, 即程序中遇到 getch(); 语句, 就会停下来, 等你按任意键, 它接收了这个字符键后再继续执行后面的代码. 这跟上面在 Windows 下用的 system("PAUSE")功能一样, 但却不会在屏幕上显示 (即不会有 "按任意键继续" 的提示), 这样, 利用 getch() 无回显的特性, 不管你按什么键, 都不会在屏幕上留下痕迹, 使你的界面达到美观效果..
getche()和 getch()很相似, 它也需要引入头文件 conio.h, 它们之间的区别就在于: getch()无回显, getche()有回显.
下面看一个例子:
首先这是个连续 5 次的循环来实现 5 次停顿, 等待你输入. 编译并运行这个程序, 假设输入的是 abcde, 那么屏幕上显示的结果也是 abcde, 这个 abcde 并不是在 ch=getch(); 中输出的. 把 printf("%c",ch); 这行语句去掉, 就会发现按 5 次任意键程序就结束了, 但屏幕上什么都没有显示.
你可以把代码中的 getch()换成 getche()看看有什么不同. 如果还是输入 abcde, 那么屏幕上显示的结果是 aabbccddee, 我们把 printf("%c",ch); 这行语句再去掉, 显示的结果就是 abcde 了, 说明程序在执行 ch=getche(); 这条语句的时候就把我们输入的键返回显示在屏幕上, 有无回显就是它们的唯一区别.
来源: http://www.jianshu.com/p/bd4fc453215b