探索scanf

记录试验过程

image-20211030000729366

​ 基于这样一道题,做如下尝试:

​ 主要想要知道,这个为什么只读入一个12.3,然后强转赋值给i以后,后面的值就不再被读入赋值给j和k了。还有就是为什么第一个浮点型会被读入,而不是识别到它与目标的格式不同就直接跳出。

第一个探索

​ 从网上了解到,scanf是有一个缓存区的,如果缓存区里面有数据,就先从缓存区里面读入,如果没有,就要求客户进行输入。读入整型或者浮点型的时候,空格,回车和Tab键都是忽略的,而输入字符串的时候,是不会被忽略的。

​ scanf是有返回值的,scanf 函数的返回值反映的是按照指定的格式符正确读入的数据的个数。
​ 如果输入数据与指定格式不符,则会产生输入错误。遇到输入错误,scanf函数会立即终止,返回已经成功读取的数据的个数。 所以,通过scanf函数的返回值和指定输入数据的个数(由格式符决定)的比较,可以判断数据输入是否成功。

​ 然后我们开始做第一个尝试:

​ 首先是返回值的尝试。

#include<stdio.h>

int main()
{
	int i=0,j=0,k=0,re=0;
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	return 0;
 }
12.3

image-20211030001915720

我们可以看到返回值是1,只有i有数值,且为12。

换几种输入方式:

12.3 12.1 12.0

image-20211030002045543

12.1 1 5

image-20211030002111049

15 12.3 5

image-20211030002334057

​ 通过以上试验,我们可以看到,不管输入多少,只要遇到浮点型,就会取整然后终止读入。不会影响它之前的读入,但之后的就不会再读入了。

第二个探索

#include<stdio.h>

int main()
{
	int i=0,j=0,k=0,re=0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	//第二次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	
	return 0;
 }
12.3

image-20211030002711828

​ 可以看到,后两次scanf都没有再读入就直接跳过了。

​ 然后我们改一下代码,让读入一次以后的i赋值为0看一下。

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	
	i = 0; 
	//第二次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	
	return 0;
 }
12.3

image-20211030003013691

​ 这样看来,跟i的值是没有关系的,也确实证明,第二次、第三次是没有数据读入的。

第三个探索

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	float fl=0.0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	i = 0; 
	//第二次 
	re = scanf("%f%d%d",&fl,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	return 0;
 }

​ 这一次的探索就有点意思了,输入第一次12.3的时候,只运行完第一次,第二次还让我继续输入,我再次输入12.3,然后结束了。

image-20211030003603568

​ 从这个结果里面,我们可以看到,12.3在被i读入取整以后,0.3被留在了缓存区,待到下一个读入%f的控制符出现的时候,会把它的小数值读入,然后会让用户继续输入数据,我再次输入12.3,j就成了12了,后面k的读入和第三次的读入都没有生效。

​ 也就是一次输入浮点型,读入却是整型的时候,小数点后面的数据会被留在缓存区。那是不是后面的数其实不是没有读入,而是读入了0.3的0呢?这个就不知道如何尝试来获得这个结论了。但我们可以输入0.3看一下结果:

image-20211030004341986

​ 由此结果,大致能表明有那么一点可能性。

​ 现在,也就是说,有两种可能:

​ 1、scanf读入的过程中,读入12.3的时候,识别到有整数部分,就继续读入,到了.3的时候,不对劲,就跳出了,并且终止scanf(是不是确实终止了,等会试试),等到.3遇到可以被读入的控制符%f的时候,可以继续读入。

​ 2、scanf读入的过程中,读入12.3的时候,识别到有整数部分,就继续读入,到了.3的时候,不对劲,然后后面的数据其实读入的是0.3的0,虽然读入了,但是这不是按照指定的格式符正确读入的数据的个数(这个一会也试试)。

第四个探索

先来第一种可能的吧:

把第一个读入的第二个控制符改为%f。

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	float fl=0.0;
	//第一次 
	re = scanf("%d%f%d",&i,&fl,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	i = 0; 
	//第二次 
	re = scanf("%f%d%d",&fl,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	return 0;
 }

image-20211030005330415

​ 事实证明,scanf并不是终止了,如果下一个是要浮点型数据的话,它会读入0.3然后继续请求数据。这个可能不太明显,我再换一个数据输入:

image-20211030005541687

这就可以明显看出,12.3是被i和fl进行读入了,然后要求输入k的值,我输入了56,也成功读入了。

也就是第一种可能比较合理的表达是:

​ 1、scanf读入的过程中,读入12.3的时候,识别到有整数部分,就继续读入,到.3的时候,识别到不是想要的类型,就终止了%d的读入,并且.3留在了缓存区里面,这个scanf语句里后面如果有%f或者%lf(这个没理由不行),那么就继续读入,说明不是直接终止了scanf语句,而是会继续执行。

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	float fl=0.0;
	
	re = scanf("%f",&fl);
	printf("fl = %f\n",fl);
	printf("re = %d\n",re);
	
	re = scanf("%d%f",&i,&fl);
	printf("i = %d\n",i);
	printf("fl = %f\n",fl);
	printf("re = %d\n",re);
	
	re = scanf("%f",&fl);
	printf("fl = %f\n",fl);
	printf("re = %d\n",re);
 }

​ 在刚才的输入输出中,发现了一个非常有意思的现象,用%f直接读入78.2的时候,输出观察到是78.199997,而通过先读入78,再读入0.2的话,就是i = 78,fl = 0.2。

​ 然后就写了这个程序单独验证一下这个现象。

image-20211030011150675

​ 它的输出确实也说明这个现象是确定的,具体原理未知。

第五个探索

第二个可能里面那个

#include<stdio.h>
int main()
{
	int i=1,j=1,k=1,re=0;
	float fl=1.0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	i = 1; 
	//第二次 
	re = scanf("%f%d%d",&fl,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	return 0;
 }

​ 这个的改动很简单,既然怀疑是后面都被0.3的0赋值了,那就初始值不设为0,用1来试。

image-20211030011746418

​ 这个结果就证明了,后面确实不是读入的0.3的0,缓存区里面更像是.3。第二个可能推翻。

第六个探索

这个是从网上看到,有一个scanf缓存区清空的代码:

rewind(stdin);

然后用这个做一个验证,它的作用跟用%f读一下其实差不多。

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	float fl=0.0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	//rewind(stdin);
 
	//第二次 
	//re = scanf("%f%d%d",&fl,&j,&k);
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	return 0;
 }

​ 注释掉rewind(stdin);的时候:

image-20211030012438010

​ 就是最开始那个效果。

#include<stdio.h>
int main()
{
	int i=0,j=0,k=0,re=0;
	float fl=0.0;
	//第一次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	rewind(stdin);
 
	//第二次 
	//re = scanf("%f%d%d",&fl,&j,&k);
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	//第三次 
	re = scanf("%d%d%d",&i,&j,&k);
	
	printf("i = %d\n",i);
	printf("j = %d\n",j);
	printf("k = %d\n",k);
	printf("re = %d\n",re);
	printf("fl = %f\n",fl);
	
	return 0;
 }

​ 现在打开那个注释:

image-20211030012629420

​ 我们可以看到,第二次的输入就不受影响了。这就说明缓存区里面的.3被清空以后,读入又重新进行了。

小结

​ scanf读入的过程中,读入12.3的时候,识别到有整数部分,就继续读入,到.3的时候,识别到不是想要的类型,就终止了%d的读入,并且.3留在了缓存区里面,这个scanf语句里后面如果有%f或者%lf(这个没理由不行)或者清空缓存区,那么就继续读入,说明不是直接终止了scanf语句,而是会继续执行。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值