版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、第十四章l 主要內(nèi)容主要內(nèi)容14.14. 常見錯誤分析常見錯誤分析14.14. 程序調(diào)試程序調(diào)試要真正學(xué)好C、用好C并不容易,“靈活”固然是好事,但也使人難以掌握,尤其是初學(xué)者往往出了錯還不知怎么回事。C編譯程序?qū)φZ法的檢查不如其他高級語言那樣嚴(yán)格(這是為了給程序人員留下“靈活”的余地)。因此,往往要由程序設(shè)計者自己設(shè)法保證程序的正確性。調(diào)試一個C程序要比調(diào)試一個PASCAL或FORTRAN程序更困難一些。需要不斷積累經(jīng)驗,提高程序設(shè)計和調(diào)試程序的水平。C語言有些語法規(guī)定和其他高級語言不同,學(xué)習(xí)過其他高級語言的讀者往往按照使用其他高級語言的習(xí)慣來寫C程序,這也是出錯的一個原因。14.1常見錯誤
2、分析下面將初學(xué)者在學(xué)習(xí)和使用C語言(不包括C+)時容易犯的錯誤列舉出來,以起提醒的作用。這些內(nèi)容在以前各章中大多已談到,為便于查閱,在本章中集中列舉,供初學(xué)者參考,以此為鑒。(1) 忘記定義變量。如:main()x=3; y=6; printf(%dn,x+y); (2) 輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。例如,若a已定義為整型,b已定義為實型。a=3;b=45;printf(%f%dn,a,b);編譯時不給出出錯信息,但運行結(jié)果將與原意不符,輸出為000000016402它們并不是按照賦值的規(guī)則進行轉(zhuǎn)換(如把45轉(zhuǎn)換成4),而是將數(shù)據(jù)在存儲單元中的形式按格式符的要求組織輸出(如b占
3、4個字節(jié),只把最后兩個字節(jié)中的數(shù)據(jù)按%d,作為整數(shù)輸出)。(3) 未注意int型數(shù)據(jù)的數(shù)值范圍。一般微型計算機上使用的C編譯系統(tǒng),對一個整型數(shù)據(jù)分配兩個字節(jié)。因此一個整數(shù)的范圍為-215215-1,即-3276832767。常見這樣的程序段:int num;num=32767;printf(%d,num+1);得到的卻是-32768,原因是32768超過32767。變成補碼形式(4) 輸入變量時忘記使用地址符。如:scanf(%d%d,a,b);這是許多初學(xué)者剛學(xué)習(xí)C語言時一個常見的疏忽,或者說是習(xí)慣性的錯誤,因為在其他語言中在輸入時只需寫出變量名即可,而C語言要求指明“向哪個地址標(biāo)識的單元送
4、值”。應(yīng)寫成scanf(%d%d,&a,&b);(5) 輸入時數(shù)據(jù)的組織與要求不符。用scanf函數(shù)輸入數(shù)據(jù),應(yīng)注意如何組織輸入數(shù)據(jù)。假如有以下scanf函數(shù):scanf(%d%d,&a,&b);有人按下面的方法輸入數(shù)據(jù):3,4這是錯的。數(shù)據(jù)間應(yīng)該用空格來分隔。讀者可以用printf(%d%d,a,b);來驗證一下。應(yīng)該用以下方法輸入:34如果scanf函數(shù)為scanf(%d,%d,&a,&b);對scanf函數(shù)中格式字符串中除了格式說明符外,對其他字符必須按原樣輸入。因此,應(yīng)按以下方法輸入:3,4此時如果用“34”反而錯了。還應(yīng)注意,不能企圖用
5、scanf(input a & b:%d,%d,&a,&b);想在屏幕上顯示一行信息:input a & b:然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以為scanf具有BASIC語言中的INPUT語句的功能(先輸出一個字符串,再輸入變量的值)。如果想在屏幕上得到所需的提示信息,可以另加一個printf函數(shù)語句:printf(input a & b:);scanf(%d,%d,&a,&b);(6) 誤把“=”作為“等于”比較符。C編譯系統(tǒng)將(a=b)作為賦值表達式處理,將b的值賦給a,然后判斷a的值是否零,若為非零,則作為“真
6、”;若為零作為假。如果a的值為3,b的值為4,ab,按原意不應(yīng)輸出“ae q u a lt ob”。而現(xiàn)在先將b的值賦給a,a也為4,賦值表達式的值為4。if語句中的表達式值為真(非零),因此輸出“ae q u a lt o b”。這種錯誤在編譯時是檢查不出來的,但運行結(jié)果往往是錯的。而且由于習(xí)慣的影響,程序設(shè)計者自己往往也不易發(fā)覺。(7) 語句后面漏分號。C語言規(guī)定語句末尾必須有分號。分號是C語句不可缺少的一部分。這也是和其他語言不同的。有的初學(xué)者往往忘記寫這一分號。如:a=3b=4編譯時,編譯程序在“a=3”后面未發(fā)現(xiàn)分號,就把下一行“b=4”也作為上一行的語句的一部分,這就出現(xiàn)語法錯誤。
7、有時編譯時指出某行有錯,但在該行上并未發(fā)現(xiàn)錯誤,應(yīng)該檢查上一行是否漏了分號。如果用復(fù)合語句,有的學(xué)過PASCAL語言的讀者往往漏寫最后一個語句的分號,如:t=a; a=b; b=t 在PASCAL中分號是兩個語句間的分隔符而不是語句的一部分,而在C中,沒有分號的就不是語句。(8) 在不該加分號的地方加了分號。例如:if(ab);printf(aislarger than bn);總之,在if、for、while語句中,不要畫蛇添足多加分號。(9) 對應(yīng)該有花括弧的復(fù)合語句,忘記加花括弧。如:sum=0;i=1;while(i=100) sum=sum+i; i+;本意是實現(xiàn)1+2+100,即i
8、。但上面的語句只是重復(fù)了sum+1的操作,而且循環(huán)永不終止。因為i的值始終沒有改變。錯誤在于沒有寫成復(fù)合語句形式。因此while語句的范圍到其后第一個分號為止。語句“i+;”不屬于循環(huán)體范圍之內(nèi)。100i=0 (10) 括弧不配對。當(dāng)一個語句中使用多層括弧時常出現(xiàn)這類錯誤,純屬粗心所致。如:while(c=getchar()!=#)putchar(c);少了一個右括弧。(11) 在用標(biāo)識符時,忘記了大寫字母和小寫字母的區(qū)別。例如:main()inta,b,c; a=2;b=3; C=A+B; printf(%d+%d=%,A,B,C); 編譯時出錯。編譯程序把a和A認(rèn)作是兩個不同的變量名處理,
9、同樣b和B,c和C都分別代表兩個不同的變量。(12) 引用數(shù)組元素時誤用了圓括弧。如:main()int i,a(10); for(i=0;i10;i+) scanf(%d,&a(i); C語言中對數(shù)組的定義或引用數(shù)組元素時必須用方括弧。(13) 在定義數(shù)組時,將定義的“元素個數(shù)”誤認(rèn)為是“可使用的最大下標(biāo)值”。main()int a10=1,2,3,4,5,6,7,8,9,10; int i; for(i=1;i=10;i+)printf(%d,ai); (14) 對二維或多維數(shù)組的定義和引用的方法不對。main()int a5,4; printf(%d,a1+2,2+2); (15
10、) 誤以為數(shù)組名代表數(shù)組中全部元素。例如:main()int a4=1,3,5,7; printf(%d%d%d%dn,a);(16) 混淆字符數(shù)組與字符指針的區(qū)別。main()char str4; str=Computer and c; printf(%sn,str); 編譯出錯。str是數(shù)組名,代表數(shù)組首地址。在編譯時對str數(shù)組分配了一段內(nèi)存單元,因此在程序運行期間str是一個常量,不能再被賦值。 (17) 在引用指針變量之前沒有對它賦予確定的值。main()char *p; scanf(%s,p); 應(yīng)當(dāng)改為char *p,c20;p=c;scanf(%s,p);(18) switch
11、語句的各分支中漏寫break語句。(18) switch語句的各分支中漏寫break語句。例如:switch(score)case 5:printf(ery good!); case 4:printf(Good!); case 3:printf(Pass!); case 2:printf(Fail!); defult:printf(data error!);原因是漏寫了break語句。case只起標(biāo)號的作用,而不起判斷作用,因此在執(zhí)行完第一個printf函數(shù)語句后接著執(zhí)行第2、3、4、5個printf函數(shù)語句。應(yīng)改為switch(score)case 5:printf(erygood!);br
12、eak; case 4:printf(Good!); break; case 3:printf(Pass!);break; case 2:print(Fail!); break; defult:print(data error!);(19) 混淆字符和字符串的表示形式。char sex;sex=M;sex是字符變量,只能存放一個字符。而字符常量的形式是用單引號括起來的,應(yīng)改為sex=M;“M”是用雙引號括起來的字符串,它包括兩個字符:M和0,無法存放到字符變量sex中。(20) 使用自加(+)和自減(-)運算符時出的錯誤。例如:main()intp,a5=1,3,5,7,9; p=a; pri
13、ntf(%d,*p+);不少人認(rèn)為“*p+”的作用是先使p加1,即指向第1個元素a1處,然后輸出第一個元素a1的值3。其實應(yīng)該是先執(zhí)行p+,而p+的作用是先用p的原值,用完后使p加1。p的原值指向數(shù)組a的第0個元素a0, (21) 有人習(xí)慣用傳統(tǒng)的方式對函數(shù)形參進行聲明,但卻把對函數(shù)的形參和函數(shù)中的局部變量混在一起定義。如:max(x,y)int x,y,;=xy?x,y; return(); 應(yīng)改為max(x,y) int x,y; int ; =xy?x:y; return(); (22) 所調(diào)用的函數(shù)在調(diào)用語句之后才定義,而又在調(diào)用前未加說明。main()float x,y,; x=35
14、;y=-76; =max(x,y); printf(%fn,); float max(float x,float y) return(=xy?x:y);這個程序乍看起來沒有什么問題,但在編譯時有出錯信息。原因是max函數(shù)是實型的,而且在main函數(shù)之后才定義,也就是max函數(shù)的定義位置在main函數(shù)中的調(diào)用max函數(shù)之后。改錯的方法可以用以下二者之一:在main函數(shù)中增加一個對max函數(shù)的聲明,即函數(shù)的原型:main() float max(float,float);/*聲明將要用到的max函數(shù)為實型*/ float x,y,; x=35;y=-76; =max(x,y); printf(%f
15、n,); 將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即:float max(float x,float y) return(=xy?x:y); main()float x,y,; x=35;y=-76; =max(x,y); printf(%fn,);這樣,編譯時不會出錯,程序運行結(jié)果是正確的。(23) 誤認(rèn)為形參值的改變會影響實參的值。main()inta,b; a=3;b=4; swap(a,b); printf(%d,%dn,a,b);swap(int x,int y)int t; t=x;x=y;y=t; 原意是通過調(diào)用swap函數(shù)使a和b的值對換,然后在main函數(shù)中輸出已對換了值
16、的a和b。但是這樣的程序是達不到目的的,因為x和y的值的變化是不傳送回實參a和b的,main函數(shù)中的a和b的值并未改變。如果想從函數(shù)得到一個以上的變化了的值,應(yīng)該用指針變量。用指針變量作函數(shù)參數(shù),使指針變量所指向的變量的值發(fā)生變化。此時變量的值改變了,主調(diào)函數(shù)中可以利用這些已改變的值。如:main()int a,b,*p1,*p2; a=3;b=4; p1=&a;p2=&b; swap(p1,p2); printf(%d,%dn,a,b);/a和b的值已對換/ swap(int *pt1, int *pt2)int t; t=*pt1;*pt1=*pt2;*pt2=t; (24
17、) 函數(shù)的實參和形參類型不一致。main()int a=3,b=4; c=fun(a,b); fun(float x,float y) 實參a、b為整型,形參x、y為實型。a和b的值傳遞給x和y時,x和y的值并非3和4。C要求實參與形參的類型一致。如果在main函數(shù)中對fun作原型聲明:fun (float, float);程序可以正常運行,此時,按不同類型間的賦值的規(guī)則處理,在虛實結(jié)合后x=3.0, y=4.0。也可以將fun函數(shù)的位置調(diào)到main函數(shù)之前,也可獲正確結(jié)果。(25) 不同類型的指針混用。main()int i=3,*p1; float a=15,*p2; p1=&i;
18、p2=&a; p2=p1; printf(%d,%dn,*p1,*p2); 企圖使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指向整型變量。指向不同類型的指針間的賦值必須進行強制類型轉(zhuǎn)換。如:p2=(float*)p1;這種情況在C程序中是常見的。例如,用malloc函數(shù)開辟內(nèi)存單元,函數(shù)返回的是指向被分配內(nèi)存空間的void *類型的指針。而人們希望開辟的是存放一個結(jié)構(gòu)體變量值的存儲單元,要求得到指向該結(jié)構(gòu)體變量的指針,可以進行如下的類型轉(zhuǎn)換。struct studentint num; char name20; float score; ;struct studentstudent
19、1,*p;p=(struct student *)malloc(LEN);p是指向struct student結(jié)構(gòu)體類型數(shù)據(jù)的指針,將malloc函數(shù)返回的void *類型指針轉(zhuǎn)換成指向struct student類型變量的指針。(26) 沒有注意函數(shù)參數(shù)的求值順序。例如有以下語句:i=3;printf(%d,%d,%dn,i,+i,+i);許多人認(rèn)為輸出必然是3,4,5實際不盡然。在Turbo C和其他一些C系統(tǒng)中輸出是5,5,4因為這些系統(tǒng)是采取自右至左的順序求函數(shù)參數(shù)的值。先求出最右面一個參數(shù)(+i)的值為4,再求出第2個參數(shù)(+i)的值為5,最后求出最左面的參數(shù)(i)的值5。C標(biāo)準(zhǔn)沒有
20、具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而左。但每個C編譯程序都有自己的順序,在有些情況下,從左到右求解和從右到左求解的結(jié)果是相同的。例如fun1(a+b,b+c,c+a);fun1是一個函數(shù)名。3個實參表達式a+b、b+c、c+a。在一般情況下,自左至右地求這3個表達式的值和自右至左地求它們的值是一樣的,但在前面舉的例子是不相同的。因此,建議最好不用會引起二義性的用法。如果在上例中,希望輸出“3,4,5”時,可以改用i=3;j=i+1;k=j+1;printf(%d,%d,%dn,i,j,k);(27) 混淆數(shù)組名與指針變量的區(qū)別。main()int i,a5; for(i=0;i5;i
21、+)scanf(%d,a+);企圖通過a的改變使指針下移,每次指向欲輸入數(shù)據(jù)的數(shù)組元素。它的錯誤在于不了解數(shù)組名代表數(shù)組首地址,它的值是不能改變的,用a+是錯誤的,應(yīng)當(dāng)用指針變量來指向各數(shù)組元素。即:int i,a5,*p;p=a;for(i=0;i5;i+)scanf(%d,p+); 或int a5,*p;for(p=a;pa+5;p+)scanf(%d,p); (28) 混淆結(jié)構(gòu)體類型與結(jié)構(gòu)體變量的區(qū)別,對一個結(jié)構(gòu)體類型賦值。struct workerlong int num; char name20; char sex; int age; ;workernum=187045;strcpy
22、(workername,ZhangFun);workersex=M;workerage=18; 這是錯誤的,只能對變量賦值而不能對類型賦值。上面只定義了struct worker類型而未定義變量。應(yīng)改為struct workerlong int num; char name20; char sex; int age; ;struct worker worker-1;worker-1num=187045;strcpy(worker-1name,Zhang Fun);worker-1sex=M;worker-1age=18; 今定義了結(jié)構(gòu)體變量worker-1,并對其中的各成員賦值。(29) 使用
23、文件時忘記打開,或打開方式與使用情況不匹配。例如,對文件的讀寫,用只讀方式打開,卻企圖向該文件輸出數(shù)據(jù),例如:if(fp=fopen(test,r)=NULL) printf(cannot open this filen); exit(0); ch=fgetc(fp);while(ch!=#) ch=ch+4; fputc(ch,fp); ch=fget(fp); 對以“r”方式(只讀方式)打開的文件,進行既讀又寫的操作,顯然是不行的。此外,有的程序常忘記關(guān)閉文件,雖然系統(tǒng)會自動關(guān)閉所用文件,但可能會丟失數(shù)據(jù)。因此必須在用完文件后關(guān)閉它。以上只是列舉了一些初學(xué)者常出現(xiàn)的錯誤,這些錯誤大多是對于
24、C語法不熟悉之故。對C語言使用多了,比較熟練了,犯這些錯誤自然就會減少了。在深入使用C語言后,還會出現(xiàn)其他一些更深入、更隱蔽的錯誤。程序出錯有三種情況: 語法錯誤。指違背了C語法的規(guī)定,對這類錯誤,編譯程序一般能給出“出錯信息”,并且告訴你在哪一行出錯。只要細心,是可以很快發(fā)現(xiàn)并排除的。 邏輯錯誤。程序并無違背語法規(guī)則,但程序執(zhí)行結(jié)果與原意不符。這是由于程序設(shè)計人員設(shè)計的算法有錯或編寫程序有錯,通知給系統(tǒng)的指令與解題的原意不相同,即出現(xiàn)了邏輯上的混亂。例如:前面第9條錯誤:sum=0;i=1;while(i=100) sum=sum+i; i+;語法并無錯誤。但while語句通知給系統(tǒng)的信息是
25、當(dāng)i100時,執(zhí)行“sum=sum+i;”。C系統(tǒng)無法辨別程序中這個語句是否符合作者的原意,而只能忠實地執(zhí)行這一指令。這種錯誤比語法錯誤更難檢查。要求程序員有較豐富的經(jīng)驗。 運行錯誤。程序既無語法錯誤,也無邏輯錯誤,但在運行時出現(xiàn)錯誤甚至停止運行。例如:int a ,b ,c;scanf(%d %d,&a,&b);c=b/a;printf(c=%dn,c); 輸入a和b的值, 輸出b/a的值, 程序沒有錯。 但是如果輸入a的值為0, 就會出現(xiàn)錯誤。 因此程序應(yīng)能適應(yīng)不同的數(shù)據(jù), 或者說能經(jīng)受各種數(shù)據(jù)的“考驗” , 具有“健壯性”。寫完一個程序只能說完成任務(wù)的一半(甚至不到一半)
26、。調(diào)試程序往往比寫程序更難,更需要精力、時間和經(jīng)驗。常常有這樣的情況:程序花一天就寫完了,而調(diào)試程序二三天也未能完。有時一個小小的程序會出錯五六處,而發(fā)現(xiàn)和排除一個錯誤,有時竟需要半天,甚至更多。希望讀者通過實踐掌握調(diào)試程序的方法和技術(shù)。16.2程 序 調(diào) 試所謂程序調(diào)試是指對程序的查錯和排錯。調(diào)試程序一般應(yīng)經(jīng)過以下幾個步驟:(1) 先進行人工檢查,即靜態(tài)檢查。在寫好一個程序以后,不要匆匆忙忙上機,而應(yīng)對紙面上的程序進行人工檢查。這一步是十分重要的,它能發(fā)現(xiàn)程序設(shè)計人員由于疏忽而造成的多數(shù)錯誤。而這一步驟往往容易被人忽視。有人總希望把一切推給計算機系統(tǒng)去做,但這樣就會多占用機器時間。而且,作為
27、一個程序人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)?shù)目茖W(xué)作風(fēng),每一步都要嚴(yán)格把關(guān),不把問題留給后面的工序。為了更有效地進行人工檢查,所編的程序應(yīng)注意力求做到以下幾點:應(yīng)當(dāng)采用結(jié)構(gòu)化程序方法編程,以增加可讀性;盡可能多加注釋,以幫助理解每段程序的作用;在編寫復(fù)雜的程序時,不要將全部語句都寫在main函數(shù)中,而要多利用函數(shù),用一個函數(shù)來實現(xiàn)一個單獨的功能。這樣既易于閱讀也便于調(diào)試,各函數(shù)之間除用參數(shù)傳遞數(shù)據(jù)這一渠道以外,數(shù)據(jù)間盡量少出現(xiàn)耦合關(guān)系,便于分別檢查和處理。(2) 在人工(靜態(tài))檢查無誤后,才可以上機調(diào)試。通過上機發(fā)現(xiàn)錯誤稱動態(tài)檢查。在編譯時給出語法錯誤的信息(包括哪一行有錯以及錯誤類型),可以根據(jù)提示的信息具體找出程序中出錯之處并改正之。應(yīng)當(dāng)注意的是:有時提示的出錯行并不是真正出錯的行,如果在提示出錯的行上找不到錯誤的話應(yīng)當(dāng)?shù)缴弦恍性僬摇A硗?,有時提示出錯的類型并非絕對準(zhǔn)確,由于出錯的情況繁多而且各種錯誤互有關(guān)聯(lián),因此要善于分析,找出真正的錯誤,而不要只從字面意義上死摳出錯信息,鉆牛角尖。如果系統(tǒng)提示的出錯信息多,應(yīng)當(dāng)從上到下逐一改正。有時顯示出一大片出錯信息往往
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024至2030年節(jié)能冰柜項目投資價值分析報告
- 墓園墓地銷售合同范例
- 電玩城轉(zhuǎn)讓合同范例
- 陜西藝術(shù)職業(yè)學(xué)院《交通工具設(shè)計(Ⅰ)》2023-2024學(xué)年第一學(xué)期期末試卷
- 陜西學(xué)前師范學(xué)院《平面分析》2023-2024學(xué)年第一學(xué)期期末試卷
- 陜西學(xué)前師范學(xué)院《電磁測量》2023-2024學(xué)年第一學(xué)期期末試卷
- 2024年車用裝貨緊線器項目可行性研究報告
- 綠化預(yù)算造價合同范例
- 2024年滾珠二折道軌項目可行性研究報告
- 2024年水平帶式洗漿機項目可行性研究報告
- 走進人工智能-AI發(fā)展史及人工智能的應(yīng)用
- 《果樹生產(chǎn)技術(shù)》實習(xí)指導(dǎo)手冊
- 西安明德理工學(xué)院
- 建筑公司對項目部對管理辦法
- 醫(yī)務(wù)科運用PDCA循環(huán)提高危急值管理合格率品管圈成果匯報
- 構(gòu)美-空間形態(tài)設(shè)計學(xué)習(xí)通課后章節(jié)答案期末考試題庫2023年
- 民法典??荚囶}及答案
- 收款賬戶確認(rèn)書
- IPTV系統(tǒng)的分析研究的開題報告
- 全北師大版英語必修一寫作+范文
- 爭做新時代好少年好隊員主題班會ppt
評論
0/150
提交評論