边界测试——让BUG现形
題目:寫一個(gè)函數(shù),輸入一行字符,將此字符串中最長(zhǎng)的單詞輸出。
| #include <stdio.h> #include <string.h> int main() {int alphabetic(char); int longest(char []); int i; char line[100]; printf("input one line:\n"); gets(line); printf("The longest word is:"); for(i=longest(line);alphabetic(line[i]);i++) printf("%c",line[i]); printf("\n"); return 0; } int alphabetic(char c) { if((c>='a'&&c<='z')||(c>='A'&&c<='z')) return(1); else return(0); } int longest(char string[]) {int len=0,i,length=0,flag=1,place=0,point; for(i=0;i<=strlen(string);i++) if(alphabetic(string[i])) if(flag) {point=i; flag=0; } else len++; else {flag=1; if(len>=length) {length=len; place=point; len=0; } } return(place); } |
運(yùn)行結(jié)果:
input a line:?????????????
I am a student.???????????
The longest word is : student
題目要求“寫一個(gè)函數(shù),輸入一行字符,將此字符串中最長(zhǎng)的單詞輸出”,可是無(wú)論alphabetic()還是longest()函數(shù)都沒(méi)有實(shí)現(xiàn)“輸入一行字符,將此字符串中最長(zhǎng)的單詞輸出”這個(gè)功能要求。疑惑很久,發(fā)現(xiàn)實(shí)現(xiàn)這個(gè)功能的函數(shù)居然是main()。這就難免讓人貽笑大方了。因?yàn)榘凑粘R?guī)的慣例,要求寫一個(gè)函數(shù)實(shí)現(xiàn)某個(gè)功能,從來(lái)不是要求寫main(),盡管不能說(shuō)main()不是“一個(gè)函數(shù)”。然而如果是要求main()完成的事情,通常是作為一個(gè)完整的問(wèn)題提出的,不會(huì)提出“寫一個(gè)函數(shù)”這樣的要求。如果硬要狡辯“寫一個(gè)函數(shù)”也不排除是寫main(),就牽強(qiáng)的近乎強(qiáng)詞奪理了。不過(guò)設(shè)若真的有人如此嘴硬,你還真拿他沒(méi)什么辦法。
既然是不見(jiàn)棺材不掉淚,那就不妨繼續(xù)往下看。
在代碼中一眼瞄見(jiàn)了flag這個(gè)變量。經(jīng)驗(yàn)表明,凡是有這個(gè)flag變量的代碼,80%以上都是垃圾代碼。道理很簡(jiǎn)單:首先,多數(shù)問(wèn)題根本不需要設(shè)置這個(gè)別別扭扭標(biāo)志變量,只有那些善于把自己的思維扭曲得如同爛麻花一樣的人才喜歡時(shí)不時(shí)地祭出flag這個(gè)破爛的法寶。其次,即使需要設(shè)置標(biāo)準(zhǔn)變量,優(yōu)秀的代碼作者也不會(huì)使用這個(gè)含義模糊不清的名字作為標(biāo)志變量名,而會(huì)用一個(gè)更貼切、意義更明確恰當(dāng)更適合描述問(wèn)題的名字。所以,一般來(lái)說(shuō),flag往往反映了代碼的垃圾度。
對(duì)于垃圾代碼,沒(méi)必要進(jìn)行過(guò)于細(xì)致的分析,只要指出錯(cuò)誤即可。不要試圖了解這種代碼的思路,因?yàn)檫@種代碼的思路本來(lái)就是錯(cuò)亂不堪的,就如同不要試圖理解瘋子的胡言亂語(yǔ)一樣。不要試圖修繕一座胡亂搭建起來(lái)的破爛不堪的危房,推倒重來(lái)才是明智的選擇。
然而,找出程序的漏洞或錯(cuò)誤,往往比完成程序要難得多。而且越是垃圾的代碼越難查錯(cuò),因?yàn)槔a往往也不具備良好的可測(cè)試性。
但是對(duì)付這種可測(cè)試性極差的垃圾代碼,有一些簡(jiǎn)單的辦法往往非常容易奏效,比如邊界檢查。訓(xùn)練有素的程序員通常都特別注意邊界,無(wú)論是寫代碼時(shí)還是檢查代碼時(shí)。因?yàn)樗麄冎肋@里非常容易出錯(cuò),而且往往失之毫厘謬之千里。但垃圾代碼的作者,由于代碼是東拼西補(bǔ)、胡亂拼湊而成的,所以往往顧不上或考慮不到這些,因此垃圾代碼很容易被“邊界檢查”這把小刀輕而易舉地戳破。以alphabetic()函數(shù)為例,只要簡(jiǎn)單地考察一下其中if語(yǔ)句所要求的表達(dá)式——(c>='a'&&c<='z')||(c>='A'&&c<='z'),就不難發(fā)現(xiàn)c<='z'這個(gè)子表達(dá)式是c<='Z'之誤。這樣就充分說(shuō)明原代碼中存在著BUG。
順便說(shuō)一下,alphabetic()函數(shù)中的if-else語(yǔ)句用得非常愚蠢,因?yàn)?c>='a'&&c<='z') || (c>='A'&&c<='Z')這個(gè)表達(dá)式的值本身就只能為0或1,所以直接返回這個(gè)表達(dá)式的值就可以了。壓根用不著脫褲子放屁地寫一個(gè)if-else語(yǔ)句。
|
或許,有人會(huì)認(rèn)為這是一個(gè)簡(jiǎn)單的筆誤或印刷錯(cuò)誤,修正了這個(gè)錯(cuò)誤原來(lái)的代碼是正確的。那么好吧,下面改正這個(gè)錯(cuò)誤后再來(lái)運(yùn)用一次簡(jiǎn)單的邊界測(cè)試。
由于問(wèn)題要求輸出一行字符中最長(zhǎng)的單詞,而一行字符中可能有0個(gè)單詞、1個(gè)單詞、2個(gè)單詞……。注意,這里0個(gè)單詞的情況就是一種邊界情況,運(yùn)行這個(gè)程序并輸入0個(gè)單詞(輸入一行不含任何字母的字符,因?yàn)榇a作者把連續(xù)的若干字母字符作為一個(gè)單詞),后果居然是——運(yùn)行時(shí)程序崩潰了。這個(gè)結(jié)果絕對(duì)可以充分說(shuō)明原來(lái)的代碼是錯(cuò)誤的。
這個(gè)結(jié)果是如何產(chǎn)生的呢?只要在紙上走查一遍,就不難發(fā)現(xiàn),輸入一行不含任何字母的字符時(shí),longest()函數(shù)中嵌套在for語(yǔ)句內(nèi)部的if語(yǔ)句將毫無(wú)意義地反復(fù)執(zhí)行
| {flag=1; if(len>=length) {length=len; place=point; len=0; } } |
部分,而其中的賦值給place的point卻居然是一個(gè)不確定的垃圾值。
應(yīng)該如何正確地給出這個(gè)問(wèn)題的代碼呢?正確解決問(wèn)題的前提是正確地提出問(wèn)題。原來(lái)問(wèn)題的提法本身就有很多不正確或不嚴(yán)謹(jǐn)?shù)牡胤健@?#xff0c;“將此字符串中最長(zhǎng)的單詞輸出”,這個(gè)要求本身就是似是而非很不明確的。比如,字符串中有兩個(gè)單詞長(zhǎng)度相同且都長(zhǎng)于其他單詞,究竟應(yīng)該輸出這兩個(gè)單詞中的任何一個(gè)還是需要同時(shí)輸出這兩個(gè)單詞?再有,要求函數(shù)“輸入一行字符”也非常無(wú)聊。為了能正確地解決問(wèn)題,有必要對(duì)原問(wèn)題的錯(cuò)誤要求進(jìn)行如下更正:
寫一個(gè)函數(shù),輸出字符串中的任一長(zhǎng)度最長(zhǎng)的單詞。這里所謂的單詞,是指不含空白字符的連續(xù)字符序列。
| #include <stdio.h>?? ??? void print_a_longestword ( const char [] ) ;?? int? be_white? ( const char )? ;?? int? find_begin( char const [] , unsigned ) ;?? int? find_end? ( char const [] , unsigned ) ;?? void output??? ( char const [] , unsigned , unsigned ) ;?? ??? int main( void )?? {?? ?? printf("%s中一最長(zhǎng)單詞為:","");??????????? //測(cè)試""??? ?? print_a_longestword("");?? ?????? ?? printf("%s中一最長(zhǎng)單詞為:"," \n\t ");????? //測(cè)試" \n\t "?? ?? print_a_longestword(" \n\t ");?? ??? ?? printf("%s中一最長(zhǎng)單詞為:"," abc ");?????? //測(cè)試" abc "?? ?? print_a_longestword(" abc ");?? ??? ?? printf("%s中一最長(zhǎng)單詞為:"," abc \tabcd? "); //測(cè)試" abc \tabcd? "?? ?? print_a_longestword(" abc \tabcd? ");?? ????????? ?? return 0;?? }?? ??? void output( char const str[] , unsigned from , unsigned to )?? {?? ?? while(from < to)?? ????? putchar(str[from ++]);?? ?? putchar('\n');????? }?? ??? int find_end (? const char str[] , unsigned from )?? {?? ????? while( str[from]!='\0' && ! be_white( str[from] ) )?? ???????? from ++ ;?? ????? return from ;????? }?? ??? int find_begin (? const char str[] , unsigned from )?? {?? ????? while( be_white( str[from] ) )?? ???????? from ++ ;?? ????? return from ;????? }?? ??? int be_white( const char c )?? {?? ?? return?????? c == ' '??? ||?? ???????????? c == '\t'? ||?? ???????????? c == '\n'? ;?? }?? ??? void print_a_longestword ( char const line[] )?? {?? ?? unsigned site = 0U ;????? ?? unsigned begin_longest , end_longest ;?? ?? begin_longest = end_longest = site??? ;?? ?????? ?? do{?? ????? int this_begin , this_end? ;?? ????????? ????? site = this_begin = find_begin ( line , site )??? ;//單詞開(kāi)頭??? ????? site = this_end? = find_end?? ( line , site )???? ;//單詞結(jié)尾???????? ??? ????? if(?? ( this_end??? - this_begin )??? ????????? > ( end_longest - begin_longest ) ){?? ???????? begin_longest = this_begin ;?? ???????? end_longest?? = this_end?? ;??????? ????? }??????? ??? ?? }while( line[ site ] != '\0') ;????? ??? ?? output( line , begin_longest , end_longest );?? } |
本文出自seven的測(cè)試人生公眾號(hào)最新內(nèi)容請(qǐng)見(jiàn)作者的GitHub頁(yè):http://qaseven.github.io/
總結(jié)
以上是生活随笔為你收集整理的边界测试——让BUG现形的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《交互式程序设计 第2版》一3.5 捕获
- 下一篇: Linux解压有思路