C语言常见问题(三)——输入字符问题,scanf、getchar与缓冲区

《Back C语言常见问题目录

目录

一、引例

二、原理

1.概念

2.缓存区的刷新

3.回过头来分析前面的程序

4.总结

三、解决办法

1.最简单的办法

2.用fflush清除缓冲区(不推荐!)

3.手动清除缓冲区(推荐写法)

四、拓展(附标准输入类型格式符)


一、引例

1.这是一个很简单的例子,读入一个整数和一个字符,并将它们打印出来

#include <stdio.h>
int main(){
	char ch;
	int num=0;
	printf("请输入一个整数: \n");
	scanf("%d",&num);
	printf("请输入一个字符: ");
	getchar();//过滤前面scanf的空格或回车
	scanf("%c",&ch);//与ch=getchar();等价
	printf("num=%d,ch=%c",num,ch);
	return 0;
}

可以看到上述例子中在第二次调用scanf读入字符时,在scanf前面多了一个getchar(),用以过滤上一个scanf遗留的回车。


如果我们把getchar()注释掉:

2.现在我们把问题简化一下,只输入一个字符并打印

#include <stdio.h>
int main(){
	char ch;
	printf("请输入一个字符: \n");
	//getchar();
	scanf("%c",&ch);//与ch=getchar();等价
	printf("ch=%c",ch);
	return 0;
}

getchar()语句被注释的情况下,可以看到程序正确地读到了字符 


 如果getchar()未被注释:

什么时候用getchar()过滤空格或回车?这里getchar()的作用和原理是什么?

要想真正弄明白这些问题,必须深入理解C语言的缓冲区的概念。


二、原理

1.概念

        在内存中预留指定大小的存储空间,用来对输入或输出的数据做临时存储,这部分预留的内存空间叫缓冲区。C语言读入/输出数据,并不是直接读入/输出的,而是先把这些数据暂存到缓冲区中,再从缓冲区中读入或打印到屏幕上(如图)。

 

        由于输入缓冲区与输出缓冲区相互独立,所以一般情况下C语言的输出函数不会影响到输入函数的执行。输出函数相对而言是不容易出错的,我们在写代码时应重点考虑scanf、getchar、gets等输入函数怎么写,才能取到我们想要的值。

2.缓存区的刷新

        程序运行时,系统会默认为标准输入/输出分配一个缓冲区,这个缓冲区的大小在头文件stdio.h中定义,通常为512字节。

        下面几种情况发生时缓冲区会刷新(清空):缓冲区满时;使用fflush函数刷新缓冲区;使用endl语句;关闭文件。

        缓冲区还有其他类型,这里主要针对的是C语言标准输入输出的缓冲区。

3.回过头来分析前面的程序

#include <stdio.h>
int main(){
	char ch;
	int num=0;
	printf("请输入一个整数: \n");
	scanf("%d",&num);
	printf("请输入一个字符: ");
	getchar();//过滤前面scanf的空格或回车
	scanf("%c",&ch);//与ch=getchar();等价
	printf("num=%d,ch=%c",num,ch);
	return 0;
}

其输入缓冲区变化如下:

执行 scanf("%d",&num);

执行 getchar();//过滤前面scanf的空格或回车

 

  

执行 scanf("%c",&ch);//与ch=getchar();等价 

 

 

解析:C语言标准输入输出函数采用行缓冲。scanf("%d",&num)语句执行后,程序读入一个整数以及回车符,缓冲区还剩下\n。此时需要getchar()函数过滤前面缓冲区中的回车,清空缓存区,显示屏才会闪烁光标提示,允许用户用键盘继续输入。这时scanf("%c",&ch)语句赋给ch的值才是用户输入的字符。而scanf语句结束后缓存区依然剩下了\n

4.总结

        在程序开始运行时,系统默认为标准输入/输出函数分配了输入输出缓冲区(需要程序包含stdio.h),这时缓冲区是空的

        第一次调用输入函数时,显示屏光标闪烁,提示用户输入。用户可以输入很长的一串字符,直至敲下回车时输入结束。这一串字符(包括回车符)会保存到缓冲区中,根据调用的输入函数以及其参数,从缓冲区中读取适当长度的一串字符给程序,同时缓冲区清除这一串送给程序的字符,这时缓冲区中仍可能剩余一些字符(比如回车符等)

        再次调用输入函数时,若缓冲区不为空,则将缓冲区中的字符返回给程序,缓冲区清除相应字符;若缓冲区为空,则显示屏光标闪烁,提示用户输入。在用户键入的一串字符包括回车符保存到缓冲区后,由调用的输入函数将指定格式的一串字符返回程序,同时缓冲区清除相应字符。

        打个比方,如果说用户在键盘上敲入的字符流是水流,键盘是注水口,而输入函数是控制水流的水龙头,那么缓冲区就是能暂时存储水的水箱。我们设想的情况是用户往注水口里注入水流,这些水会暂时保存在水箱里。在水龙头打开的时候,水流从水箱流向水龙头。

        当我们打开水龙头(调用输入函数)时,有两种情况:1.如果水箱里没水(缓冲区为空),(程序)就会请求用户往注水口里注水(键盘输入字符)。注水完成后,注入的水并不会直接从水龙头出来,而是先流入水箱(缓冲区)。由水龙头阀门打开的大小(被调用的输入函数的格式)决定水龙头的水流量,水龙头流出多少体积的水,那水箱就相应减少多少体积的水。当关闭水龙头时,水箱中仍可能有剩余一些水。2.如果水箱里有剩余的水,那么水龙头优先获取水箱里暂存的水,这时(程序)不会请求用户往注水口里注水(键盘输入字符)。

        总的来说,关于输入函数要把握三个要点:1.键盘输入的值先暂存到输入缓冲区中;2.键盘输入结束的回车符会一起保存到缓冲区中;3.一般在输入函数结束后,缓冲区仍有剩余字符(很有可能是回车符)。


三、解决办法

1.最简单的办法

        根据输入情况和输入函数调用情况,分析缓冲区,在恰当的时候用getchar()过滤多余的字符。

2.用fflush清除缓冲区(不推荐!)

        在输入函数后面使用fflush(stdin);语句可以清空缓冲区。

        但是fflush(stdin)有一个严重的问题!!!

        那就是C和C++的标准里从来没有定义过fflush(stdin)。fflush 是对C标准的扩充,某些编译器(如VC6)支持用 fflush(stdin)来清空输入缓冲,但是并非所有编译器都支持这个功能,比如gcc3.2就不支持。

3.手动清除缓冲区(推荐写法)

#include <stdio.h>
int main(){
	char ch;
	int num=0;
	printf("请输入一个整数: \n");
	/*清除缓冲区缓冲*/
	if (scanf("%d", &num)!=EOF){
		while ((ch=getchar())!='\n' && ch!=EOF);
	}
	/*清除缓冲区缓冲end*/
	printf("请输入一个字符: \n");
	scanf("%c",&ch);
	printf("num=%d,ch=%c",num,ch);
	return 0;
}

四、拓展(附标准输入类型格式符)

1.下面举了一个例子,帮助你更好地理解输入缓冲区

#include <stdio.h>
int main(){
	char ch,ch1,ch2;
	printf("请输入一个字符: \n");
	ch=getchar();
	ch1=getchar();
	ch2=getchar();
	printf("|ch=%c,ch1=%c,ch2=%c|",ch,ch1,ch2);
	return 0;
}

//输入asdfg[回车]

 

 //输入a[回车]s[回车]

解析:输入函数返回多长的数据,缓冲区就相应缩短多少,直至缓冲区为空时刷新,此时(缓冲区为空时)若还有输入函数待执行,光标会闪烁等待用户输入

2.一个特殊的例子,scanf读入字符格式为"\n%c"或" %c" 

#include <stdio.h>
int main(){
	char ch;
	int num=0;
	printf("请输入一个整数: \n");
	scanf("%d",&num);
	printf("请输入一个字符: \n");
	scanf("\n%c",&ch);//scanf(" %c",&ch);
	printf("|num=%d,ch=%c|",num,ch);
	return 0;
}

3. 输入类型格式符

1)有符号类型

int(%d)、short(%hd)、long(%ld)、long long(%lld)、char(%c、%d、%u)、

float(%f)、double(%lf)、long double(%Lf)

2)无符号类型

unsigned char(%c、%d、%u)、unsigned short(%hu、%ho、%hx)、unsigned int(%u,、%o、%x)、unsigned long(%lu、%lo、%lx)、unsigned long long(%llu、%llo、%llx),

一些编译器支持不在C标准中的类型,例如VC++6.0支持64位整数_int64(%I64d)

  • scanf读入多个数据举例(类型格式符之间无空格):
int v1=0,v2=0;
float v3=0;
scanf("%d%d%f",&v1,&v2,&v3);//多变量读入
printf("v1=%d,v2:%d,v3为%f\n",v1,v2,v3);//输出

  • double类型变量输入输出:
double x=0;
scanf("%lf",&x);//读入double类型变量,格式符为%lf
printf("%.2f",x);//输出double类型变量,格式符为%f,使用.n控制精度

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易水卷长空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值