读到这篇文章的人90%的可能是遇到了输入字符后,回车没有预期的输出,连续回车都没用。这种情况可能是因为你在scanf中加了\n,而且加了不该加的地方。把代码里的\n删了或者再输入一个不是\n的字符再回车。(回车会作为\n记录在缓存)
下面来分析一下为什么会发生这种情况
scanf不是直接从键盘获取数据的,scanf和键盘之间还存在一个媒介。当程序执行到scanf时,会从你的缓冲区读东西,如果缓冲区是空的,就进入阻塞状态。基于此点我们导论%c %d %s %f
\n与%c:
%c获取的是字符,‘\n’自然也是它的目标之一。我们来看下面的代码。为了展示效果,我们把scanf放在循环里。在打印时我们将打印字符使用*包围。
#include <stdio.h>
int main(int argc, char const *argv[])
{
char get;
while(1){
scanf("%c",&get);
printf("*%c*\n",get);
}
return 0;
}
编译后执行,输入1 回车,发现打印了两份内容,1和回车。在很多场景下这是我们无法接受的结果。
如何解决这种局面:
在scanf("%c",&get);中加上\n变成scanf("\n%c",&get),一定要加在%c前面。为什么不能加在%c后面我们文章末尾再讨论。我们看下加载%c前面会发生什么。首先\n在这里会匹配到缓冲区中0个或多个\n,当首次运行时%c匹配到我输入的1,由于监测到了\n(回车),触发了缓冲刷新,缓冲把1赋值给了get,缓冲区剩下一个\n,此时进入下一个循环,在下一循环中scanf中的\n匹配到了缓冲区的\n,此时重点来了,scanf把\n获取过来丢了(连续的\n都会被匹配到),\n不会被赋给任何变量,然后我们再输入一个字符,被%c获取并赋值给get变量。
\n 与%d %s %f
对于%s 和 %d %f而言,\n就显得不那么重要了,这两种格式化方式会忽略\n,当监测到回车时随即刷新缓冲,对于这三种方式而言遇到\n或者空格即表示一串连续字符的结束。如果加入到循环里就会像下面以下获取数据。
从上面的图可以看出未被匹配走的\n或者空字符是依旧保留在缓冲的。只不过再获取的时候在缓冲前面的连续\n或空字符被忽略掉了。对于强迫症而言这是极其不适的。当然你也可以像上面提到的%c做法在%d前面加上\n和空格,对结果没什么影响,不信你试试。
但是我们通常不这么做。
通用做法
我们常用的方式是使用while循环结合getchar()将缓冲清空。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int get;
while(1){
scanf("\n %d",&get);
printf("*%d*\n",get);
while('\n' != getchar()){//清空缓冲
continue;
}
}
return 0;
你也也已简写成
#include <stdio.h>
int main(int argc, char const *argv[])
{
int get;
while(1){
scanf("\n %d",&get);
printf("*%d*\n",get);
while('\n' != getchar());//清空缓冲
}
return 0;
来解释以下添加这句之后发生了什么。getchar()是用来获取字符的,在scanf把需要的数据获取走之后,getchar()在没有获取到\n之前处于一个死循环状态,不停从缓冲获取数据,直到遇到了\n,不符合循环条件,跳出循环,进入下一次scanf,scanf继续等待输入。这里必须注意,虽然\n导致循环结束,但是\n被getchar()取走已经是不争的事实。此时缓冲空空如也,没有\n,没有\n,没有\n。当然除非你\n后面还有一个\n。
\n在格式化符号后面发生什么
上面我们强调了\n必须要%c前面,不能再%c后面,我们看下如果再后面会发生什么。%c预定了1,\n预定了\n,缓冲刚准备刷新,突然发现这个\n居然是老大需要的,这个\n不是用来刷新的,那就等着吧,等等等,突然遇到了一个\n,触发刷新,然后1给了%c,\n给了\n然后丢了,最后缓冲区剩下2和\n,你可能好奇2和\n也满足%c\n的格式呀,为什么没有被直接输出,因为%c\n代表的并不是两个字符,它可能对应到缓冲区的%c\n\n\n(无数个\n),因为在scanf中的\n对应的是缓冲中的连续多个\n,在这种情况下不能保证你是否已经输入完了。(所以不要在纠结为什么上面只有%c时,输入一个字符再回车,会把字符和回车打印出来,而不是像这种情况只打印一次了,应为只有%c的话它代表的就是一个字符,没必要再等一次回车去进行刷新,只要回车前面的每一个字符都是%c需要的)
,
这也就是为什么你在输入1之后连续回车都没有用,应为你连续的\n都被一个\n接收了,只有输入一个不是\n的字符比如上面的2,打断这种连续。