C语言中scanf()函数对程序健壮性不良影响的解决办法

2016-06-14 00:00韩平
电脑知识与技术 2016年12期

韩平

摘要:C语言中格式输入函数scanf() 的格式控制字符串中除格式说明符以外的其他字符都必须由用户从键盘原样输入,否则,就会出现意想到的结果或程序运行意外中止。C语言中的gets(char s[])函数接受键盘输入或已经存在于输入缓冲区中的所有字符,包括回车符,但回车符不写入字符串s中,而是在遇到回车符时,为s添加字符串结束标志符\0。该文通过实例,利用do-while循环,结合gets()函数,给出了一个解决格式输入函数scanf()对程序健壮性不良影响问题的办法。

关键词:程序健壮性;输入缓冲区;scanf();gets();fflush(stdin)

中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)12-0107-02

Abstracts: All the format-control strings in scanf() of C language must be inputted into computer as they are by user from keyboard ,except for format specifiers. Otherwise, it can lead to some unexpected results or the program terminates unexpectedly. The function, gets (char s[]) of C Language, accepts keyboard input or all the characters in the input buffer, including carriage returns. Carriage return isnt written in s, and system adds the end symbol ‘\0 of string when encountering a carriage return. This paper gives a solution to the unwelcome effects on the robustness of program caused by scanf () of C language through do-while loop structure and gets(char s[]) function used in some examples .

Key words: the robustness of program; input buffer; scanf(); gets();fflush(stdin)

1 C语言中的scanf()函数对程序的健壮性会产生不良影响

C语言的输入输出都是通过函数来实现的,其中最常用的就是格式输入函数scanf()。但这个函数的不良使用会给程序的健壮性带来不好的影响。例如,程序pro1.c(设定银行定期存款的年利率为2.25%,已知存款期为n年,存款本金为captial元,编程计算并输出n年后的本利之和deposit[1])没有考虑到用户可能误输入非法字符的情况,其健壮性显然不够好。

2 scanf()函数对程序健壮性不良影响分析

函数scanf() 的格式控制字符串中除格式说明符以外的其他字符都必须由用户从键盘原样输入[1]。如果用户在输入数据时没有对格式说明符以外的其他字符原样输入,就会出现意想不到的结果或程序运行意外中止。因此我们在使用scanf()函数时先做到如下三点:一、调用一次scanf()函数只输入一个数据;二、scanf()函数中不出现格式说明符以外的字符;三、每次输入数据前都先输出一条提示信息给用户。即便这样,用户仍然可能会误输入非法字符。Scanf()遇到空格符、回车符、制表符和非法字符都会认为数据输入结束。对于程序pro1.c,如果程序运行到第一条scanf()函数调用语句时,用户输入“500abc”,由于输入的字符“a” 、“b”、“c”非法,所以系统认为数据输入结束。变量capital可以得到值500.0,“abc”还在输入缓冲区中。程序运行到第二个scanf()函数调用语句时,系统不再等待用户从键盘输入数据,而是直接从输入缓冲区中取数据给变量n。由于字符“a” 、“b”、“c”都不是数字字符而是非法字符,所以scanf()函数调用失败,也就为变量n取值失败,但“abc”仍然在输入缓冲区中。这时程序算是意外结束,得到的运行结果也会让初学者感到迷茫。如果程序运行到第一个scanf()函数调用语句时用户输入数据合法,在程序运行到第二个scanf()函数调用语句时,用户输入“2abc”或“2.3”,因为n为int型,所以“abc”和“.3”为非法字符,系统认为数据输入结束。变量n可以得到合法的值2,“abc”或“.3”仍在输入缓冲区中。此时程序运行虽然正常结束,但这个结果同样会令初学者感到迷茫。在此,给出防止用户输入非法字符而影响程序健壮性的办法。除了格式说明符%s外,此方法对其它格式说明符都有效。

3 scanf()函数对程序健壮性不良影响的解决办法

3.1利用gets()函数解决scanf()函数对程序健壮性不良影响的问题

gets(char s[])函数是C语言中的一个常用字符串输入函数。它接受键盘输入或已经存在于输入缓冲区中的所有字符,包括回车符,但回车符不写入字符串s中,而是在遇到回车符时,为s添加字符串结束标志符\0。因此可以通过do——while循环,结合gets()函数来解决上文中所提到的问题。在每个scanf()函数调用语句后都跟一条gets(s)函数调用语句,这样就可以把所有非法数据放入s中,同时也就清空了输入缓冲区;把这两条库函数调用语句放入do——while循环中,只要用户输入非法数据,字符串s的长度就会大于0,用户就必须重新输入数据,直到某次输入的数据完全合法为止(这时字符串s为空串,其长度为0)。

需要注意的是,字符数组s的长度一定要足够长,否则程序运行也会意外中止。

3. 2输入缓冲区的清空

关于上文提到的清空输入缓冲区的问题,fflush(stdin)函数也是可以做到的。有时候残留在输入缓冲区中的数据垃圾会被下一个变量接收,但这会造成程序运行意外中止或得不到我们想要的结果,这时就需要事先清空输入缓冲区,可以选择使用fflush(stdin)函数来完成这个工作。例如程序pro3.c,运行该程序时,假定用户在输入变量ascii的值时完全正确,但用户却永远没有机会从键盘输入变量ch的值。程序的运行结果总是“很遗憾,您答错了!”这显然不是我们想要的结果。我们来分析一下为什么会得到这样的运行结果。

通过scanf("%c",&ch)使变量ch获取数据时,空格符、制表符和回车符都被当做有效字符。当程序运行到scanf(“%d”,&ascii)函数调用语句时,假定用户输入“49”,然后敲回车键。这时,变量ascii获得值49,但回车符(其ASCII 码为10)仍然在输入缓冲区中。当程序运行到scanf(“%c”,&ch)函数调用语句时,系统直接从输入缓冲区中取数据给变量ch,而不是让用户从键盘输入。因此,变量ch得到的值就是回车符。回车符的ASCIIi码与9个数字字符的ASCII码都不相等,因此,程序运行的结果总是“很遗憾,您答错了!”如果改写程序pro3.c为程序pro4.c,就可以解决这个问题。

虽然只添加了一条fflush(stdin);语句,但由于这个fflush(stdin)函数是可以清空输入缓冲区的,这样,输入缓冲区中的数据垃圾就不会被变量ch接收了,变量ch的值也就只能通过键盘来输入了。如果用户输入的字符的ASCII码与前面输入的变量ascii的值一样,该程序的输出结果就是“太棒了,您答对了!”。如果用户输入的字符的ASCII码与前面输入的变量ascii的值不一样,该程序的输出结果就是“很遗憾,您答错了!”

但不是所有的C编译系统都提供这个功能,因此,通过fflush(stdin)函数来清空输入缓冲区的程序,其可移植性不够好[1]。总之我还是建议用gets()函数来解决问题,既可以防止用户误输入非法字符,又可以清空输入缓冲区。

4 scanf()函数不适合于包含空格或制表符的字符串

如果想通过scanf(“%s”,str)函数输入字符串,那么str得到的字符串中不可能含有空格符或制表符,因为scanf()函数遇到空格符和制表符都会认为数据输入结束。因此,要想使str获得包含空格符或制表符的字符串,使用gets(str)函数比较好。

参考文献:

[1] 苏小红, 王宇颖, 孙志岗.C语言程序设计[M]. 2版.北京: 高等教育出版社, 2013.