scanf中‘\n‘的用法和隐患

读到这篇文章的人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,打断这种连续。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值