韓平
摘要:C語言中格式輸入函數(shù)scanf() 的格式控制字符串中除格式說明符以外的其他字符都必須由用戶從鍵盤原樣輸入,否則,就會(huì)出現(xiàn)意想到的結(jié)果或程序運(yùn)行意外中止。C語言中的gets(char s[])函數(shù)接受鍵盤輸入或已經(jīng)存在于輸入緩沖區(qū)中的所有字符,包括回車符,但回車符不寫入字符串s中,而是在遇到回車符時(shí),為s添加字符串結(jié)束標(biāo)志符\0。該文通過實(shí)例,利用do-while循環(huán),結(jié)合gets()函數(shù),給出了一個(gè)解決格式輸入函數(shù)scanf()對(duì)程序健壯性不良影響問題的辦法。
關(guān)鍵詞:程序健壯性;輸入緩沖區(qū);scanf();gets();fflush(stdin)
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):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()函數(shù)對(duì)程序的健壯性會(huì)產(chǎn)生不良影響
C語言的輸入輸出都是通過函數(shù)來實(shí)現(xiàn)的,其中最常用的就是格式輸入函數(shù)scanf()。但這個(gè)函數(shù)的不良使用會(huì)給程序的健壯性帶來不好的影響。例如,程序pro1.c(設(shè)定銀行定期存款的年利率為2.25%,已知存款期為n年,存款本金為captial元,編程計(jì)算并輸出n年后的本利之和deposit[1])沒有考慮到用戶可能誤輸入非法字符的情況,其健壯性顯然不夠好。
2 scanf()函數(shù)對(duì)程序健壯性不良影響分析
函數(shù)scanf() 的格式控制字符串中除格式說明符以外的其他字符都必須由用戶從鍵盤原樣輸入[1]。如果用戶在輸入數(shù)據(jù)時(shí)沒有對(duì)格式說明符以外的其他字符原樣輸入,就會(huì)出現(xiàn)意想不到的結(jié)果或程序運(yùn)行意外中止。因此我們在使用scanf()函數(shù)時(shí)先做到如下三點(diǎn):一、調(diào)用一次scanf()函數(shù)只輸入一個(gè)數(shù)據(jù);二、scanf()函數(shù)中不出現(xiàn)格式說明符以外的字符;三、每次輸入數(shù)據(jù)前都先輸出一條提示信息給用戶。即便這樣,用戶仍然可能會(huì)誤輸入非法字符。Scanf()遇到空格符、回車符、制表符和非法字符都會(huì)認(rèn)為數(shù)據(jù)輸入結(jié)束。對(duì)于程序pro1.c,如果程序運(yùn)行到第一條scanf()函數(shù)調(diào)用語句時(shí),用戶輸入“500abc”,由于輸入的字符“a” 、“b”、“c”非法,所以系統(tǒng)認(rèn)為數(shù)據(jù)輸入結(jié)束。變量capital可以得到值500.0,“abc”還在輸入緩沖區(qū)中。程序運(yùn)行到第二個(gè)scanf()函數(shù)調(diào)用語句時(shí),系統(tǒng)不再等待用戶從鍵盤輸入數(shù)據(jù),而是直接從輸入緩沖區(qū)中取數(shù)據(jù)給變量n。由于字符“a” 、“b”、“c”都不是數(shù)字字符而是非法字符,所以scanf()函數(shù)調(diào)用失敗,也就為變量n取值失敗,但“abc”仍然在輸入緩沖區(qū)中。這時(shí)程序算是意外結(jié)束,得到的運(yùn)行結(jié)果也會(huì)讓初學(xué)者感到迷茫。如果程序運(yùn)行到第一個(gè)scanf()函數(shù)調(diào)用語句時(shí)用戶輸入數(shù)據(jù)合法,在程序運(yùn)行到第二個(gè)scanf()函數(shù)調(diào)用語句時(shí),用戶輸入“2abc”或“2.3”,因?yàn)閚為int型,所以“abc”和“.3”為非法字符,系統(tǒng)認(rèn)為數(shù)據(jù)輸入結(jié)束。變量n可以得到合法的值2,“abc”或“.3”仍在輸入緩沖區(qū)中。此時(shí)程序運(yùn)行雖然正常結(jié)束,但這個(gè)結(jié)果同樣會(huì)令初學(xué)者感到迷茫。在此,給出防止用戶輸入非法字符而影響程序健壯性的辦法。除了格式說明符%s外,此方法對(duì)其它格式說明符都有效。
3 scanf()函數(shù)對(duì)程序健壯性不良影響的解決辦法
3.1利用gets()函數(shù)解決scanf()函數(shù)對(duì)程序健壯性不良影響的問題
gets(char s[])函數(shù)是C語言中的一個(gè)常用字符串輸入函數(shù)。它接受鍵盤輸入或已經(jīng)存在于輸入緩沖區(qū)中的所有字符,包括回車符,但回車符不寫入字符串s中,而是在遇到回車符時(shí),為s添加字符串結(jié)束標(biāo)志符\0。因此可以通過do——while循環(huán),結(jié)合gets()函數(shù)來解決上文中所提到的問題。在每個(gè)scanf()函數(shù)調(diào)用語句后都跟一條gets(s)函數(shù)調(diào)用語句,這樣就可以把所有非法數(shù)據(jù)放入s中,同時(shí)也就清空了輸入緩沖區(qū);把這兩條庫函數(shù)調(diào)用語句放入do——while循環(huán)中,只要用戶輸入非法數(shù)據(jù),字符串s的長度就會(huì)大于0,用戶就必須重新輸入數(shù)據(jù),直到某次輸入的數(shù)據(jù)完全合法為止(這時(shí)字符串s為空串,其長度為0)。
需要注意的是,字符數(shù)組s的長度一定要足夠長,否則程序運(yùn)行也會(huì)意外中止。
3. 2輸入緩沖區(qū)的清空
關(guān)于上文提到的清空輸入緩沖區(qū)的問題,fflush(stdin)函數(shù)也是可以做到的。有時(shí)候殘留在輸入緩沖區(qū)中的數(shù)據(jù)垃圾會(huì)被下一個(gè)變量接收,但這會(huì)造成程序運(yùn)行意外中止或得不到我們想要的結(jié)果,這時(shí)就需要事先清空輸入緩沖區(qū),可以選擇使用fflush(stdin)函數(shù)來完成這個(gè)工作。例如程序pro3.c,運(yùn)行該程序時(shí),假定用戶在輸入變量ascii的值時(shí)完全正確,但用戶卻永遠(yuǎn)沒有機(jī)會(huì)從鍵盤輸入變量ch的值。程序的運(yùn)行結(jié)果總是“很遺憾,您答錯(cuò)了!”這顯然不是我們想要的結(jié)果。我們來分析一下為什么會(huì)得到這樣的運(yùn)行結(jié)果。
通過scanf("%c",&ch)使變量ch獲取數(shù)據(jù)時(shí),空格符、制表符和回車符都被當(dāng)做有效字符。當(dāng)程序運(yùn)行到scanf(“%d”,&ascii)函數(shù)調(diào)用語句時(shí),假定用戶輸入“49”,然后敲回車鍵。這時(shí),變量ascii獲得值49,但回車符(其ASCII 碼為10)仍然在輸入緩沖區(qū)中。當(dāng)程序運(yùn)行到scanf(“%c”,&ch)函數(shù)調(diào)用語句時(shí),系統(tǒng)直接從輸入緩沖區(qū)中取數(shù)據(jù)給變量ch,而不是讓用戶從鍵盤輸入。因此,變量ch得到的值就是回車符?;剀嚪腁SCIIi碼與9個(gè)數(shù)字字符的ASCII碼都不相等,因此,程序運(yùn)行的結(jié)果總是“很遺憾,您答錯(cuò)了!”如果改寫程序pro3.c為程序pro4.c,就可以解決這個(gè)問題。
雖然只添加了一條fflush(stdin);語句,但由于這個(gè)fflush(stdin)函數(shù)是可以清空輸入緩沖區(qū)的,這樣,輸入緩沖區(qū)中的數(shù)據(jù)垃圾就不會(huì)被變量ch接收了,變量ch的值也就只能通過鍵盤來輸入了。如果用戶輸入的字符的ASCII碼與前面輸入的變量ascii的值一樣,該程序的輸出結(jié)果就是“太棒了,您答對(duì)了!”。如果用戶輸入的字符的ASCII碼與前面輸入的變量ascii的值不一樣,該程序的輸出結(jié)果就是“很遺憾,您答錯(cuò)了!”
但不是所有的C編譯系統(tǒng)都提供這個(gè)功能,因此,通過fflush(stdin)函數(shù)來清空輸入緩沖區(qū)的程序,其可移植性不夠好[1]??傊疫€是建議用gets()函數(shù)來解決問題,既可以防止用戶誤輸入非法字符,又可以清空輸入緩沖區(qū)。
4 scanf()函數(shù)不適合于包含空格或制表符的字符串
如果想通過scanf(“%s”,str)函數(shù)輸入字符串,那么str得到的字符串中不可能含有空格符或制表符,因?yàn)閟canf()函數(shù)遇到空格符和制表符都會(huì)認(rèn)為數(shù)據(jù)輸入結(jié)束。因此,要想使str獲得包含空格符或制表符的字符串,使用gets(str)函數(shù)比較好。
參考文獻(xiàn):
[1] 蘇小紅, 王宇穎, 孫志崗.C語言程序設(shè)計(jì)[M]. 2版.北京: 高等教育出版社, 2013.