在之前的(一)提到的关于scanf的标准输入缓冲区中,我们每次进行数据的输入都是单次读取,即每次输入完后,希望程序再次运行再来一遍输入过程的时候,必须结束掉当前输入后再次运行程序,这给我们的输入带来的极大的不便,那么有没有更好一点的方法呢?这就需要将我们的scanf与while循环进行结合使用,例如这样:
while中是判断循环是否结束的条件,括号内条件为真循环继续执行,直到为假停止执行。我们之前讲到,scanf返回的是成功读入的参数的个数,eof是定义的一个常量,值为-1。
这里我的理解是这样的,例如我们中学阶段一个非常常用的计算圆的面积公式:S=πr^2,当写作3.14*r^2时,我们不太好联想到这是关于计算圆的面积的公式,而若是写作πr^2,那我们联想到它和圆的面积的计算应该就不难理解了,因此eof也是我们率先定义的一个常量,这个常量经常用于判断条件真假。
当每次的输入成功的个数不等于eof时,也就是我们有成功输入的数据,我们就把他打印出来。 而上图中我们的代码是每次输入整数,若是一不小心手滑输入了一个字符,或是一个浮点数,毫无疑问是不会正常打印的,那么会出现什么样的情况呢?
前两个分别输入10,20,还是正常打印出a=10,a=20:
那么当我输入a,很明显我输入的是个字符,并不是符合条件的%d整形,看结果:
不断重复打印a=20,陷入了一个死循环,这是怎么回事?为什么打印的是之前的20?接下来直接上图:
这是第一次的输入,输入数字10,敲击换行,对应到我们的输入缓冲区:
scanf读取然后匹配,10符合%d整形要求,但由于scanf的行缓冲机制,还没有进行真正的IO操作,读取到换行符才会进行IO操作,所以当我们输入10敲击换行,10被打印出来,即“a=10”,缓冲区只剩下了换行符\n:
接下来输入的是20,敲击回车,\n并不是整形数据,之前讲到scanf当读取输入整形、字符时会“清空”缓冲区内的换行与空格(详见(一)),因此清空\n缓冲区变为空,我们输入的20和\n取而代之,同上,输入完后“a=20”被成功打印,留下\n在缓冲区内:
接下来重点来了,我们输入了一个a,敲击回车,可是a是一个字符,scanf读取输入肯定无法与之匹配。先看看缓冲区:
scanf无法与之匹配,很明显,没有被成功读入,while的循环条件始终成立,而下次匹配,a和\n仍然存在于缓冲区里,仍然没有成功读入,循环条件成立,因此始终执行“a=”的打印,而最后一次我们给i的值是20,我们敲击回车后进行了真正的IO操作,把20成功读走,因此会死循环,重复不停打印a=20。
那有没有什么办法可以解决这一问题?答案是肯定的,我们写一接口rewind(stdin),其中stdin是标准输入(standard input)的“缩写”(其实不能叫缩写,应该叫做标准输入的简明表示形式),这一接口的目的就是每次循环前,“重洗”(清楚一遍缓冲区),根据rewind这个单词可以说是见名知其义了,有很多接口大家可以根据其表示单词的意思去记忆,死记硬背是很容易遗忘的。对于使用苹果系统的同学,大多是使用vscode编码,目前没有办法,只能关闭再运行,尽量保证自己不要出错,对于使用Windows的同学,如果是vs2019的,可以用这个接口rewind,而对于2012或者比较早的,会使用fflush(stdin),这个就更简明了,“冲洗”缓冲区。这里就不再演示fflush的用法。
对于始终输入正确的想要进行终止,我们连按两下ctrl+z即可,苹果仍然没有办法。