#include <stdio.h>int main(){ int a; char c; do{ scanf("%d", &a); scanf("%c", &c); printf("a = %d c = %c ", a, c); /* printf("c = %d ", c); */ } while(c != 'N'); return 0;}
scanf("%c", &c);这句不能正常接收字符,什么原因呢?我们用printf("c = %d ", c);将C用int表示出来,启用printf("c = %d ", c);这一句,看看scanf()函数赋给C到底是什么,结果是c=10 ,ASCII值为10是什么?换行即 .对了,我们每击打一下"Enter"键,向键盘
缓冲区发去一个“回车”( ),一个“换行"( ),在这里 被scanf()函数处理掉了(姑且这么认为吧^_^),而 被scanf()函数“错误”地赋给了c.解决办法:可以在两个scanf()函数之后加个
fflush(stdin);,还有加
getch(),
getchar()也可以,但是要视具体scanf()语句加那个,这里就不分析了,读者自己去摸索吧。但是加
fflush(stdin);不管什么情况都可行。
#include <stdio.h>int main(){ int a; char c; do{ scanf("%d", &a); fflush(stdin); scanf("%c", &c); fflush(stdin); printf("a=%d c=%c ",a,c); } while(c!='N'); return 0;}
版本1:运行出错的程序
#include <stdio.h>int main(){ int i; char j; for (i = 0; i < 10; ++i) scanf("%c", &j); /* 这里%前没有空格 */ printf("%c", j); /* 在输入十个字符之后 */ return 0;}
版本2:使用了空格控制符后
#include <stdio.h>int main(){ int i; char j; for (i = 0; i < 10; ++i) scanf(" %c", &j);/* 注意这里%前有个空格 */ printf("%c", j);/* 在输入十个字符之后,验证打印出来的字符是否是自己输入的最后一个字符(即输入的第十个字符)*/ return 0;}
接着,我们运行看看,首先,运行第一个版本(错误的程序)
我们输入:
0 1 2 3 4 5 6 7 8 9
结果是一个空字符
再运行第二个版本(正确的程序)
同样输入:
0 1 2 3 4 5 6 7 8 9
那么为什么第二个程序就正确呢,原因何在,在%前面加一个空格就这么有用,答案是肯定的,就是%前面的空格在起作用,读者看看此文章的前面部分,在scanf的使用过程中应注意的问题中已经指出:“scanf()的格式控制串可以使用空白
字符或其它非空白字符,使用空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符。”
所以在%前面加上了空格(空格属于空白
字符,此外还有像
制表符等也属于空白字符),在输入过程中,将略去输入中的一个或多个空白字符,所以我们输入的0 1 2 3 4 5 6 7 8 9这些字符中的空白字符就被略去了,字符9也就正确的打印出来了,这样子解释,相信大家都看明白勒吧!
问题三
输入类型与格式化字符串不匹配导致stdin流的阻塞。
#include <stdio.h>int main(){ int a=0,b=0,c=0,ret=0; ret=scanf("%d%d%d",&a,&b,&c); printf("第一次读入数量:%d ",ret); ret=scanf("%d%d%d",&a,&b,&c); printf("第二次读入数量:%d ",ret); return0;}
我们定义了a,b,c三个变量来接受输入的内容,定义了变量ret来接收scanf函数的返回值。
正确输入的话:
但是当输入内容与格式换字符串不匹配时,结果会令人大跌眼镜(仔细分析会对scanf函数和stdin流有更深入的哦):
执行到第一个scanf时,当输入字符’b’的时候与ret=scanf("%d%d%d",&a,&b,&c);中的格式化字符串不匹配,stdin流被阻塞,scanf函数不在读取后面的部分,直接将1返回,表示只将stdin流中的1读入到了变量a中。
执行到第二个scanf时,字符’b’还是与格式化字符串不匹配,stdin流仍然被阻塞,所以没有提示输入,scanf函数将0返回。
将代码作如下修改,可以有力的证明上述结论。
#include <stdio.h>int main(){ int a=0,b=0,c=0,ret=0; ret=scanf("%d%d%d",&a,&b,&c); printf("第一次读入数量:%d ",ret); ret=scanf("%c%d%d",&a,&b,&c); printf("第二次读入数量:%d ",ret); return0;}
当把第二个scanf函数内的格式化字符串改为”%c%d%d”时,运行结果如下:
执行到第一个scanf函数时,由于输入’b’的原因scanf函数直接返回1,stdin流阻塞。
执行到第二个scanf函数时,字符’d’与格式化字符串”%c%d%d”中的%c匹配,stdin流终于疏通,在输入6,则将变量a,b,c分别赋值为98(‘b’的ASCII码)、2、6,scanf函数返回3。
有上述问题可知,当使用scanf函数时,如果遇到一些匪夷所思的问题,在scanf函数后正确使用fflush(stdin);,清空输入缓冲区,可以解决很多问题。以本题为例:
#include <stdio.h>int main(){ int a=0,b=0,c=0,ret=0; ret=scanf("%d%d%d",&a,&b,&c); fflush(stdin); printf("第一次读入数量:%d ",ret); ret=scanf("%d%d%d",&a,&b,&c); fflush(stdin); printf("第二次读入数量:%d ",ret); return0;}
运行结果:
问题解决。
问题四
如何处理scanf()函数误输入造成程序死锁或出错
#include <stdio.h>int main(){ int a, b, c; scanf("%d,%d", &a, &b); c = a + b; /*计算a+b*/ printf("%d + %d = %d", a, b, c); return 0;}
如上程序,如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个错误的结果,呵呵,这可能所有人都遇到过的问题吧?解决方法:scanf()函数执行成功时的返回值是成功读取的
变量数,也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘
缓冲区就可能还个有残余信息问题。正确的例程:
#include <stdio.h>int main(){ int a,b,c; while (scanf("%d,%d", &a, &b) != 2) fflush(stdin); c = a + b; printf("%d + %d = %d", a, b, c); return 0;}
补充
int fflush(FILE *stream);
如果stream指向输出流或者更新流(update stream),并且这个更新流
执行的操作不是输入,那么
fflush函数将把任何未被写入的数据写入stream
指向的文件(如标准输出文件stdout)。否则,
fflush函数的行为是不确定的。
函数会给那些流打上错误标记,并且返回EOF,否则返回0。
由此可知,如果 stream 指向输入流(如 stdin),那么
fflush 函数的行为是不确定的。故而使用
可采用如下方法:
方法一:
/* 此函数可以和scanf函数一起使用,但使用%c输入时要注意,即此函数只能用于缓冲区非空的情况 */#include <stdio.h>void flush(){ char c; while ((c = getchar()) != ' ' && c != EOF) ;}int main(){ int a,b,c; /*计算a+b*/ while (scanf("%d,%d", &a, &b) != 2) flush(); c = a + b; printf("%d + %d = %d", a, b, c);}
方法二:
程序示例:
#include <stdio.h>int main(){ int i, c; while (1 ) { printf("Please input an integer: "); scanf("%d", &i); if (feof(stdin) || ferror(stdin)) { /* 如果用户输入文件结束标志(或文件已被读完),或者发生读写错误,则退出循环 */ /* do something */ break; } /* 没有发生错误,清空输入流。通过 while 循环把输入流中的余留数据“吃”掉 */ while ( (c = getchar()) != ' ' && c != EOF ) ; /*可直接将这句代码当成fflush(stdin)的替代,直接运行可清除输入缓存流,使用 scanf("%*[^ ]"); 也可以清空输入流,不过会残留 字符。 */ printf("%d ", i); } return 0;}发展
使用scanf函数进行输入,必须指定输入的数据的类型和格式,不仅繁琐复杂,而且很容易出错.C++保留scanf只是为了和C兼容,以便过去用C语言写的程序可以在C++的环境下运行。C++的编程人员都愿意使用cin进行输入,很少使用scanf。