关于scanf、getchar、getch、getche缓冲区分析——C语言
缓冲区
根据数据刷新的时机可以将缓冲区的类型分为:全缓冲、行缓冲、无缓冲
(注意:Windows下的输出设备没有缓冲区,意思是printf是无缓冲的,但是在Linux下printf就是行缓冲的,至于为什么Windows下printf是无缓冲的,后文会提到)
全缓冲:当缓冲区被填满以后才进行真正的输入输出操作
行缓冲:当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作
无缓冲:没有缓冲区,立即进行输入输出

(图片来源:https://www.cnblogs.com/mydomain/p/9817320.html)
(缓冲区其实是一块内存空间,它用在硬件设备和用户程序之间,用来缓存数据,目的是让快速的 CPU 不必等待慢速的输入输出设备,同时减少操作硬件的次数)
为什么Windows下C语言的printf是无缓冲的?
C语言作为一个面向过程的语言,算不上高级语言也不能说是低级语言,C语言完成的工作大多都是偏底层开发工作(再底层一点可能就是汇编了),C语言常常用在嵌入式、单片机之类的小缓冲区的东西上,
嵌入式设备的缓冲区通常远小于PC,如果printf是行缓冲的,可能刚写进去一点东西,就可能被别的玩意儿盖掉了,所以嵌入式做开发带流的函数进行I/O的时候通常一次I/O马上就要fflush()
缓冲区的刷新遵循以下的规则
1、不管是行缓冲还是全缓冲,缓冲区满时会自动刷新
2、行缓冲遇到换行符\n时会刷新
3、关闭文件时会刷新缓冲区
4、程序关闭时一般也会刷新缓冲区,这个是由标准库来保障的
5、使用特定的函数也可以手动刷新缓冲区
C语言标准规定,输入输出缓冲区要具有以下特征:
1、当且仅当输入输出不涉及交互设备时,它们才可以是全缓冲的
2、错误显示设备不能带有缓冲区
所谓交互设备,就是现代计算机上的显示器和键盘。C标准虽然规定它们不能是全缓冲的,但并没有规定它们到底是行缓冲还是无缓冲,这就导致不同的平台有不同的实现
输入设备
Windows、Linux、Mac OS 在实现时都给输入设备带上了行缓冲,所以 scanf()、getchar()、gets() 在每个平台下的表现都一致
Windows 下特有的 getche() 和 getch()函数,都不带缓冲区
输出设备
Windows 平台下,输出设备是不带缓冲区的
Linux 和 Mac OS 平台下,输出设备带有行缓冲区
scanf函数——行缓冲
这个可能是最常用到的输入函数了,scanf() 是从标准输入设备(键盘)读取数据,当遇到 scanf() 函数时,程序会先检查输入缓冲区中是否有数据,是带有行缓冲区的,如果没有,就等待用户输入,
用户从键盘输入的每个字符都会暂时保存到缓冲区,直到按下回车键,产生换行符('\n'),输入结束
需要注意的是:当输入的数据与存储该数据的变量类型不符合,scanf() 还会尝试忽略一些空白符,例如空格、制表符、换行符等
例如:
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d%d", &a, &b);
printf("%d %d", a, b); return ;
}
如果输入1(空格)2,中间的空格会被自动忽略到,也就是上面所说的会尝试忽略掉一些空白符(像前面这样的数字(空格)数字输入格式,无论中间输入多少的空白符会被忽略)
scanf还有一个比较奇怪的特性:只有当控制字符串以格式控制符开头时,才会忽略换行符、空格、制表符等空白符,为了读者理解这句话的含义,下面上代码
情况一:不会忽略换行符
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d", &a);
scanf("b=%d", &b);
printf("%d %d", a, b); return ;
}
当输入1回车时,回车符虽然是属于空白符,但是此时不能被忽略,不能被忽略的换行符('\n')和格式控制字符串%d又不匹配,所以只能读取失败了,b的值不变还是20

情况二:会忽略换行符
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d", &a);
scanf("%d", &b);
printf("%d %d", a, b); return ;
}
此时控制字符串变成以格式控制字符串(%d)开头,此时输入1回车,第二个scanf("%d", &b)就会把回车符('\n')忽略掉,等待用户输入下一个数字(在输入1回车后无论输入多少个空格\制表符\回车都会被忽略掉)

下面来分析下scanf("%s")输入字符串时的特点
scanf使用格式控制字符串%s读入字符串时,遇到回车、空格、制表符结束输入,下面将举多个栗子
来分析scanf输入字符串时的特点
例一:
#include <stdio.h>
#include <Windows.h> int main()
{
char ss[][]; scanf("%s%s", ss[], ss[]); printf("%s%s\n", ss[], ss[]); return ;
}
输入为:Hello(空格)World

从结果可以看出,中间的空格被忽略掉了,现在来分析下过程:
前半部分的 Hello(空格) 碰到空格时就结束第一次输入,将Hello存储在ss[0]这个字符串中(注意:此时空格符仍然存在于缓冲区中,并没有被读走)继续读取第二个字符串,
此时scanf会先瞧一瞧缓冲区中有没有东西,发现里面有一个空格符,忽略掉,继续读取World,发现后面有个换行符,结束第二次输入,将World存储在ss[1]这个字符串中,换行符仍然留在缓冲区
(这里的特点和输入单个字符串一致,即在输入第一个非空白符前,输入多少个空格符(空格\制表符\回车)都会被忽略掉)
getchar()——行缓冲
getchar()可以看作scanf("%c");的一个简化版本,它和scanf("%c")的特性相同,这里就不再赘述了
gets()函数——行缓冲
gets(ss)遇到回车结束输入,读取的换行符被转换为NULL值,做为字符数组的最后一个字符,来结束字符串(意思就是将'\n'变成了'\0'),gets()以回车作为字符串的终止符,同时将回车符从输入缓冲区读走,
但不作为字符串的一部分。而scanf()不读走回车符,回车符仍留在输入缓冲区中,除了输入结束标志的不同外,gets和scanf在读取字符串时还有一个很大的区别,gets在读入字符串时不会忽略掉空白符,
意思是如果有语句gets(ss[0]),如果只按一下回车,那么回车就被当作字符串读入ss[0]这个字符串中,而不像scanf那样在读入到第一个非空白符之前忽略掉输入的所有空白符
代码示例:
#include <stdio.h>
#include <Windows.h> int main()
{
char ss[][]; gets(ss[]);
gets(ss[]); printf("%s%s", ss[], ss[]); return ;
}
getch()函数——不带缓冲区(无缓冲)
函数功能:当有输入时,立即读取(不需要按回车),不显示在屏幕上(不带回显),需要注意的是这个函数并非标准函数,跨平台使用时需要考虑移植性(getch() 位于 conio.h 头文件中,而这个头文件是 Windows 特有的,
Linux 和 Mac OS 下没有包含该头文件。换句话说,getche() 并不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用)
getche()函数——不带缓冲区(无缓冲)
函数功能:当有输入时,立即读取(不需要按回车),并显示在屏幕上(带回显),和getch()一样位于conio.h中,是非标准函数,认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用
关于清空缓冲区的三种方法
在谈到清空缓冲区的方法时,可能很多读者都会想到fflush(stdin)来清空缓冲区,但是fflush(stdin)存在一个问题,就是C语言标准规定,当 fflush() 用于stdout时,必须要有清空输出缓冲区的作用
但是C语言标准并没有规定 fflush() 用于stdin时的作用,编译器的实现者可以自由决定,所以它的行为是未定义的
(fflush(stdin) 这种不标准的写法只适用于一部分编译器,通用性比较差)
方法一:使用getchar()来清空缓冲区
特点:getchar() 是带有缓冲区的,并且一切字符通吃,或者说一切字符都会读取,不会忽略
int c;
while((c = getchar()) != '\n' && c != EOF);
方法二:使用scanf()来清空缓冲区
scanf("%*[^\n]"); scanf("%*c");
上面的语句分成两部分来看,第一部分:scanf("%*[^\n]"),这个语句作用是读取缓冲区中回车符('\n')之前的所有字符,并丢弃,在遇到回车符('\n')时便停止读取(注意:此时缓冲区中还有一个回车符),
第二部分:scanf("%*c"),这个语句的作用是读取一个字符,并丢弃,这样就将缓冲区中仅剩的一个回车符也读走了,此时就已经清空了缓冲区
方法三:使用fflush(stdin)来清空缓冲区
fflush(stdin);
不太推荐使用这种方法(还是因为通用性的问题,建议使用前面两种方法,因为前两种方法在任何平台、任何编译器、任何情景下都有效)
关于scanf、getchar、getch、getche缓冲区分析——C语言的更多相关文章
- 区分getchar(),getch(),getche()三个函数:
区分getchar(),getch(),getche()三个函数: 第一行是手动输入的,第二行是printf输出的. getch()和getche()这两个函数使用时要包含conio.h头文件: ge ...
- getchar() getch() getche() gets() puts() scanf()的用法及区别
getchar() putchar(ch) scanf() 头文件stdio.h getch() getche() 头文件conio.h gets() puts() 头文件stdio.h ...
- getch(),getche(),getchar()的区别
先说基本区别. (1) getch()和getche()函数 这两个函数都是从键盘上读入一个字符.其调用格式为: getch(); getche(); 两者的区别是 ...
- getchar() 、 scanf() 、流与缓冲区
C中的缓冲区一直是debug的重灾区,今天在写一个命令行界面的时候又遇到了这个问题,所以来总结一波. 两函数的不同之处 scanf() 会把 stdinBuff 中的特定格式数据取出,非特定格式数据则 ...
- scanf()/getchar()和gets()深入分析
C/C++学习笔记1 - 深入了解scanf()/getchar()和gets()等函数 ---------------------------------------------------- | ...
- 深入了解scanf()/getchar()和gets()/cin等函数
转:http://www.cnblogs.com/FCWORLD/archive/2010/12/04/1896511.html 转:问题描述一:(分析scanf()和getchar()读取字符) s ...
- 深入了解scanf() getchar()和gets()等函数之间的区别
scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的.但是有时候却就是因为使用这些函数除了问题,却找不出其中的原因.下面先看一个很简单的程序: 程序1 ...
- geotrellis使用(十)缓冲区分析以及多种类型要素栅格化
目录 前言 缓冲区分析 多种类型要素栅格化 总结 参考链接 一.前言 上两篇文章介绍了如何使用Geotrellis进行矢量数据栅格化以及栅格渲染,本文主要介绍栅格化过程中常用到的缓冲区分 ...
- geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言 ...
随机推荐
- AJAX-DOM事件
1.DOM事件 1.select的onchange事件 当选项框中的内容发生改变时需要出发的事件.2.Ajax 1.名词解释 1.同步 在一个任务进行中,不能开启其它的任务. 同步访问:浏览器在向服务 ...
- ThinkPHP getBy动态查询
getBy动态查询 ThinkPHP getBy动态查询是一个魔术方法,可以根据某个字段名称动态得到对应的一条数据记录. 根据用户名(username)查询对应的用户资料记录: public func ...
- Codeforces 1106 简要题解
文章目录 A题 B题 C题 D题 E题 F题 传送门 A题 传送门 读错题还能过样例我给自己点个赞. 题意简述:给一个010101网格SSS,问满足Si,j=Si+1,j+1=Si+1,j−1=Si− ...
- JPA错误
2016-11-141.2016-10-31: hibernate用注解 一对多 报Could not determine type for错误 原因: 接下来继续解决第二个问题:怎么又与集合打交道 ...
- 树状数组(hdu-4325,hdu-1166,pat-1057)
1.hdu-4325 题意:插花,要么给出插花的范围,要么查询某个点的花的个数. 思路:通过更新,每次更新区间S到T的数值,表插入花(这一点一开始没想到), 要么查询某个点的花的数目. (与以往单纯的 ...
- ubuntu彻底删除apache2 再重装
删除apache2不彻底,导致用 apt-get install apache2 重新装时总是不成功.下面是如何彻底删除apache2 1. 删除apache 代码: $ sudo apt-get - ...
- javascript 连等赋值问题
var a = {n:1}; var b = a; // 持有a,以回查 a.x = a = {n:2}; alert(a.x);// --> undefined alert(b.x);// - ...
- DC画线
CClientDC hdc(this);//获取DC CPen pen(PS_SOLID,4,RGB(255,0,0));//创建一支红笔 CPen * pOldPen=hdc.SelectObjec ...
- 【Javascript第二重境界】序
JS是个人比较喜欢的一门语言,在前端开发中也处于核心位置.前面断断续续的研究了一段时间,这其中包括 对象,原型,继承,函数,设计模式,模块,DOM操作,以及其它又多又琐碎的知识点,而且大部分内容都没有 ...
- 2.3.5使用原子类进行i++操作
除了在i++操作时使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现 原子操作时不可分割的整体,没有其他线程能够中断或检查正在原子操作的变量,一个原子类型 ...