【C语言输入函数区分】scanf() gets() gechar() 超详解,通俗易懂

说明: 本篇文章主要是为了区分输入格式问题,就是空格录不录入,回车录不录入等问题。

一、scanf()函数

  1. 正常scanf()输入情况

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s%s",s1,s2);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符串S2 -> %s\n",s2);
    	return 0;
    } 
    
    

    在这里插入图片描述
    在这里插入图片描述
    即:这时候无论字符串中间有多少空格和回车,scanf都会自动忽略,即在缓冲区里面,遇到回车或换行都是自动忽略的。

  2. 如果如同输入1这样的输入,串1和串2之间的空格还在吗?

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s%s",s1,s2);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符串S2 -> %s\n",s2);
    	char c1=getchar();
    	char c2=getchar();
    	printf("字符C1   -> %d\n",c1);
    	printf("字符C2   -> %d",c2);
    	return 0;
    } 
    
    

    如果空格在的话,那么这个c1就应该是空格,c2就应该是回车,而且最终是不要再输入一个字符的。
    在这里插入图片描述
    在这里插入图片描述
    这里,输入字符串后,会需要在输入一个字符,也就是说当前缓冲区里面的字符数量是只有一个的,打印出来的ASCII是10,代表回车。也就是说多个字符串如果被输入了,之间的空格或回车都没了,只剩下目前缓冲区的字符。
    当然还有一种情况:
    在这里插入图片描述
    当后面输入了ddd的时候,d会作为第二个字符输入。因为空格和 ‘ d ’ 都在缓冲区内。

综上: 至此,我们可以得出,对于scanf输入字符串,它是以空格和回车作为结束标志,且结束标志是不会被录入的。所以最后的(注意是最后的)结束标志还在缓冲区内。

那么有一个问题就油然而生了,就是中间的字符,比如空格回车等,他们是消失了吗?能不能获取呢?

  1. 获取中间字符

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s",s1);
    	char c1=getchar();
    	scanf("%s",s2);
    	char c2=getchar();
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符串S2 -> %s\n",s2);
    	printf("字符C1   -> %d\n",c1);
    	printf("字符C2   -> %d",c2);
    	return 0;
    } 
    
    

    在这里插入图片描述
    也就是中间字符是可以获取的。但是如果输入的中间字符过多,从而超出了c1的容纳范围会报错吗?
    在这里插入图片描述
    不会报错的。

  2. 匹配问题

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s   %s",s1,s2);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符串S2 -> %s\n",s2);
    	char c1=getchar();
    	char c2=getchar();
    	printf("字符C1   -> %d\n",c1);
    	printf("字符C2   -> %d",c2);
    	return 0;
    } 
    
    

    这里%s %s之间有3个空格,如果输入小于3个空格或者大于3个输入还正确吗 ?
    在这里插入图片描述
    可见输入1个空格是可以运行的,这个就不像其他那种%d (=) %d验证格式的输入了。
    在这里插入图片描述
    同理,大于3个空格也可以运行 。

  3. 循环输入问题

    #include<stdio.h>
    int main(){
    	char str[20];
    	while(scanf("%s",str)!=EOF){
    		
    	}
    	return 0;
    } 
    

    scanf()的循环输入是以EOF结尾的。

总结:对于scanf(),%s之间有多少个空格,是不关心的,而且,直到最后一个字符串输入完毕后,结束标志,是不会录入的。
在这里插入图片描述
如图,如果想录入两个字符串aaa sss,最后的缓冲区中会是这个样子的:
在这里插入图片描述
如果getchar的话,会获得空格。

#include<stdio.h>
int main(){
	char s1[20],s2[20];
	scanf("%s   %s",s1,s2);
	printf("字符串S1 -> %s\n",s1);
	printf("字符串S2 -> %s\n",s2);
	char c1=getchar();
	char c2=getchar();
	char c3=getchar();
	printf("字符C1   -> %d\n",c1);
	printf("字符C2   -> %d\n",c2);
	printf("字符C2   -> %d",c3);
	return 0;
} 

获得输入完之后的3个字符,是 ‘space’ ‘space’ ’ d ’
在这里插入图片描述

二、gets()函数

  1. 正常情况

    #include<stdio.h>
    int main(){
    	char s1[20];
    	gets(s1);
    	printf("字符串S1 -> %s\n",s1);
    	return 0;
    } 
    

    在这里插入图片描述
    正常情况下会连空格一起输入进去,gets()函数以回车为结束标志。

  2. gets()函数会不会接受回车符呢?

    #include<stdio.h>
    #include<string.h>
    int main(){
    	char s1[20];
    	gets(s1);
    	printf("please enter the second letter:\n");
    	char c1=getchar();
    	printf("字符串S1 -> %d\n",s1[0]);
    	printf("字符C1   -> %d\n",c1);
    	printf("please enter the third letter:\n");
    	char c3=getchar();
    	return 0;
    } 
    

    在这里插入图片描述
    这里我一开始啥也不输入,是不会提示我输入第二个字符的,当我输入一个回车的时候,提示我输入第二个字符,然后程序就暂停了,也就是当时的缓冲区是空的(因为如果缓冲区不是空的,这个getchar会自动执行,直接从缓冲区拿一个字符),所以会提示我输入字符,这时我再输入一个回车,提示我输入第三个字符,也就是我的猜测正确了,即缓冲区是空的,而这时,注意到字符串的第一个位置ASCII是0,就是空的意思,并不是回车的ASCII,即回车其实并没有被录入到gets中的字符串中去,字符串还是空的。

    所以: 得出一个结论,gets()函数不会接受回车字符,但是gets函数可以去掉一个回车,至于为什么不去掉多个回车,我想上面的案例足够清楚了,如果一个gets可以去掉多个回车,那么第二个回车字符是不会被c1得到的。

  3. 多个gets()连用

    #include<stdio.h>
    #include<string.h>
    int main(){
    	char s1[20],s2[20],s3[20];
    	gets(s1);
    	gets(s2);
    	gets(s3);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符串S2 -> %s\n",s2);
    	printf("字符串S3 -> %s\n",s3);
    	printf("please enter the letter:\n");
    	char c3=getchar();
    	printf("字符C3   -> %d\n",c3);
    	return 0;
    } 
    

    在这里插入图片描述
    这里我连用3个gets(),其中第一个字符串输入后我按了两次回车,显示结果如下,也就是,第二个gets()是处理了第二个回车的,这时注意到,最后sss输入结束后按了回车,显示了三个字符串情况,又提醒输入字符,也就是缓冲区是空的了,也就意味着,最后一个字符串输入时按得那个回车没了。综上,又得出一个结论,gets()函数,处理回车的方法,就是把回车从缓冲区中拿出去,即删掉,但是并不会将回车放入数组中,就是扔掉了。

  4. 循环输入问题

    #include<stdio.h>
    int main(){
    	char s[20];
    	freopen("in.txt","r",stdin);
    	while(gets(s)!=0){
    		puts(s);
    	}
    	return 0;
    } 
    

    或者

    #include<stdio.h>
    int main(){
    	char s[20];
    	freopen("in.txt","r",stdin);
    	while(gets(s)!=NULL){
    		puts(s);
    	}
    	return 0;
    } 
    

    在这里插入图片描述
    因为gets函数返回值是,赋值后字符串的首地址,如果赋值失败,则返回空,而空的ASCII就是0 。
    这里注意一个问题:

    1. 赋值成功时,gets返回值与数组首地址是相同的:

      #include<stdio.h>
      int main(){
      	char s[20];
      	freopen("in.txt","r",stdin);
      	char *x;
      	printf("s数组的首地址    -> %d\n",s);
      	printf("\n");
      	while((x=gets(s))!=NULL){
      		printf("gets的返回值地址 -> %d\n",x);
      	}
      	return 0;
      } 
      

      在这里插入图片描述

    2. 赋值失败时,gets返回NULL(空),而数组首地址还是原来的地址,它们就不同了。

      #include<stdio.h>
      int main(){
      	char s[20];
      	freopen("in.txt","r",stdin);
      	char *x;
      	printf("s数组的首地址    -> %d\n",s);
      	printf("\n");
      	x=gets(s);
      	printf("gets的返回值地址 -> %d\n",x);
      	return 0;
      } 
      

      在这里插入图片描述

    3. 很重要的一个伴生问题。
      不知道读者有没有注意到,我用的是文件输入。如果用控制台输入会怎么样?

      #include<stdio.h>
      int main(){
      	char s[20];
      	char *x;
      	printf("s数组的首地址    -> %d\n",s);
      	printf("\n");
      	x=gets(s);
      	printf("gets的返回值地址 -> %d\n",x);
      	return 0;
      } 
      

      在这里插入图片描述
      在把文件输入去掉后,返回的地址又一样了,这就很迷惑,我认为这是一种很抽象的问题。应该做如下解释。

      使用文件输入时,缓冲区是这样的。
      在这里插入图片描述
      gets获取不到任何换行符,所以返回NULL。

      使用控制台输入时。
      在这里插入图片描述
      其实缓冲区是有东西的,所以,形式上,gets应该去给数组赋值,然后处理掉换行符。而gets确实也这么做了,只不过字符数组的内容为空而已,但是确确实实给数组赋了值。所以返回的地址是一样的。

      $ 控制台输入后的字符数组长啥样?

      #include<stdio.h>
      int main(){
      	char s[20];
      	char *x;
      	printf("s数组的首地址    -> %d\n",s);
      	printf("\n");
      	x=gets(s);
      	printf("字符串s第一位    -> %d\n",s[0]);
      	printf("gets首地址       -> %d\n",x);
      	printf("字符串x第一位    -> %d\n",x[0]);
      	return 0;
      } 
      

      打印看一下字符数组的第一个字符是啥?
      在这里插入图片描述
      很惊喜,是NULL,也就是空。

综上: 可以得出,gets()函数会读取空格,并以回车为结束标志,一个gets()一次读入一行,并且顺带处理掉行尾的回车换行符。所以,我们不用再去单独用getchar处理换行符了

三、scanf() gets() gechar() 三种函数混用

这里只举一个例子,就会很简单的明白了。

  1. 正确情况

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s",s1);
    	char c1=getchar();
    	gets(s2);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符C1   -> %d\n",c1);
    	printf("字符串S2 -> %s\n",s2);
    	return 0;
    } 
    

    在这里插入图片描述
    因为我们知道scanf不会处理换行符,所以要用getchar将换行符处理掉,才可以用gets

  2. 特殊情况

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	scanf("%s",s1);
    	char c1=getchar();
    	gets(s2);
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符C1   -> %d\n",c1);
    	printf("字符串S2 -> %s\n",s2);
    	return 0;
    } 
    

    在这里插入图片描述
    代码还是上面的代码,但是输入情况就不一样了,不是说gets是输入一行吗?为啥是sss呢?

    其实所有的一切,都归结于一个道理,就是缓冲区目前长啥样的问题。下面 3 进行汇总详细解释。

  3. 从缓冲区角度理解问题
    无论上面的第一种情况还是第二种情况,从缓冲区角度看是一样的。

    1. 第一种情况缓冲区:
      在这里插入图片描述
    2. 读入aaa
      在这里插入图片描述
      getchar用来处理换行符
    3. getchar处理后
      在这里插入图片描述
      这时候才是gets的真正的读取一行的效果,至于如何判断是否为一行,就是从当前开始,直到遇到第一个换行符为止。
    4. 输入完成后
      在这里插入图片描述
      因为gets将换行符处理了。
  4. 为了更好地理解,这里在多说一点,如果代码是这个样子

    #include<stdio.h>
    int main(){
    	char s1[20],s2[20];
    	gets(s2);
    	char c1=getchar();
    	scanf("%s",s1);
    	char c2=getchar();	
    	printf("字符串S1 -> %s\n",s1);
    	printf("字符C1   -> %c\n",c1);
    	printf("字符串S2 -> %s\n",s2);
    	printf("字符C2   -> %d\n",c2);
    	return 0;
    } 
    

    在这里插入图片描述
    如果代码是先gets后scanf,缓冲区是什么样子呢?

    1. 缓冲区状态
      在这里插入图片描述
    2. gets完成后
      在这里插入图片描述
      gets完成后是没有换行符的
    3. getchar输入c1完成后
      在这里插入图片描述
    4. scanf完成后
      在这里插入图片描述
    5. 最后剩下一个换行符,因为scanf不处理换行符,所以由getchar处理掉,最终缓冲区空。
      在这里插入图片描述

好了,到这里,整理就结束了,是不是感觉理解的更深一层

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值