版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、第16章 常見錯(cuò)誤和程序調(diào)試,16.1 常見錯(cuò)誤分析16.2 程序調(diào)試,要真正學(xué)好C、用好C并不容易,“靈活”固然是好事,但也使人難以掌握,尤其是初學(xué)者往往出了錯(cuò)還不知怎么回事。C編譯程序?qū)φZ(yǔ)法的檢查不如其他高級(jí)語(yǔ)言那樣嚴(yán)格(這是為了給程序人員留下“靈活”的余地)。因此,往往要由程序設(shè)計(jì)者自己設(shè)法保證程序的正確性。調(diào)試一個(gè)C程序要比調(diào)試一個(gè)PASCAL或FORTRAN程序更困難一些。需要不斷積累經(jīng)驗(yàn),提高程序設(shè)計(jì)和調(diào)試程序的水平。
2、C語(yǔ)言有些語(yǔ)法規(guī)定和其他高級(jí)語(yǔ)言不同,學(xué)習(xí)過其他高級(jí)語(yǔ)言的讀者往往按照使用其他高級(jí)語(yǔ)言的習(xí)慣來寫C程序,這也是出錯(cuò)的一個(gè)原因。,16.1 常見錯(cuò)誤分析下面將初學(xué)者在學(xué)習(xí)和使用C語(yǔ)言(不包括C++)時(shí)容易犯的錯(cuò)誤列舉出來,以起提醒的作用。這些內(nèi)容在以前各章中大多已談到,為便于查閱,在本章中集中列舉,供初學(xué)者參考,以此為鑒。(1) 忘記定義變量。如:main( ) ?。鹸=3; y=6; printf(&qu
3、ot;%d\n ",x+y); },C要求對(duì)程序中用到的每一個(gè)變量都必須定義其類型,上面程序中沒有對(duì)x、y進(jìn)行定義。應(yīng)在函數(shù)體的開頭加int x,y;這是學(xué)過BASIC和FORTRAN語(yǔ)言的讀者寫C程序時(shí)常見的一個(gè)錯(cuò)誤。在BASIC語(yǔ)言中,可以不必先定義變量類型就可直接使用。在FORTRAN中,未經(jīng)定義類型的變量按隱含的I-N規(guī)則決定其類型,而C語(yǔ)言則要求對(duì)用到的每一個(gè)變量都要在本函數(shù)中定義(除非已定義為外部變量)。
4、(2) 輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。例如,若a已定義為整型,b已定義為實(shí)型。,a=3;b=4.5;printf("%f %d\n",a,b);編譯時(shí)不給出出錯(cuò)信息,但運(yùn)行結(jié)果將與原意不符,輸出為0.000000 16402它們并不是按照賦值的規(guī)則進(jìn)行轉(zhuǎn)換(如把4.5轉(zhuǎn)換成4),而是將數(shù)據(jù)在存儲(chǔ)單元中的形式按格式符的要求組織輸出(如b占4個(gè)字節(jié),只把最后兩個(gè)字節(jié)中的數(shù)據(jù)按%d,作為整數(shù)輸出)
5、。(3) 未注意int型數(shù)據(jù)的數(shù)值范圍。一般微型計(jì)算機(jī)上使用的C編譯系統(tǒng),對(duì)一個(gè)整型數(shù)據(jù)分配兩個(gè)字節(jié)。因此一個(gè)整數(shù)的范圍為,-215~215-1,即-32768~32767。常見這樣的程序段:int num; num=89101; printf("%d",num);得到的卻是23565,原因是89101已超過32767。兩個(gè)字節(jié)容納不下89101,則將高位截去。見圖16.1。即將超過低16位的數(shù)截去。即
6、將89101減去216(即16位二進(jìn)制所形成的模)。89101-65536=23565。,圖16.1有時(shí)還會(huì)出現(xiàn)負(fù)數(shù)。例如num=196607;輸出得-1。因?yàn)?96607的二進(jìn)制形式為00 00 00 00 00 00 00 1011 11 11 11 11 11 11 11去掉高位10,低16位的值是-1(-1的補(bǔ)碼是:1111111111111111)?! ?duì)于超過整個(gè)范圍的數(shù),要用long型,即改為long int
7、 num;num=89101;,printf("%ld",num);請(qǐng)注意,如果只定義num為long型,而在輸出時(shí)仍用“%d”說明符,仍會(huì)出現(xiàn)以上錯(cuò)誤。(4) 輸入變量時(shí)忘記使用地址符。如:scanf("%d%d",a,b);這是許多初學(xué)者剛學(xué)習(xí)C語(yǔ)言時(shí)一個(gè)常見的疏忽,或者說是習(xí)慣性的錯(cuò)誤,因?yàn)樵谄渌Z(yǔ)言中在輸入時(shí)只需寫出變量名即可,而C語(yǔ)言要求指明“向哪個(gè)地址標(biāo)識(shí)的單元送值”。應(yīng)寫成
8、scanf("%d%d",&a,&b);(5) 輸入時(shí)數(shù)據(jù)的組織與要求不符。用scanf函數(shù)輸入數(shù)據(jù),應(yīng)注意如何組織輸入,數(shù)據(jù)。假如有以下scanf函數(shù):scanf("%d%d",&a,&b);有人按下面的方法輸入數(shù)據(jù):3,4這是錯(cuò)的。數(shù)據(jù)間應(yīng)該用空格來分隔。讀者可以用printf("%d%d",a,b);來驗(yàn)證一下。應(yīng)該用以下
9、方法輸入:34如果scanf函數(shù)為scanf("%d,%d",&a,&b);對(duì)scanf函數(shù)中格式字符串中除了格式說明符外,,對(duì)其他字符必須按原樣輸入。因此,應(yīng)按以下方法輸入:3,4此時(shí)如果用“34”反而錯(cuò)了。還應(yīng)注意,不能企圖用scanf("input a & b:%d,%d",&a,&b);想在屏幕上顯示一行信息:input a &
10、amp; b:然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以為scanf具有BASIC語(yǔ)言中的INPUT語(yǔ)句的功能(先輸出一個(gè)字符串,再輸入變量的值)。如果想在屏幕上得到所需的提示信息,可以另加一個(gè)printf函數(shù)語(yǔ)句:printf("input a & b:");,scanf("%d,%d",&a,&b);(6) 誤把“=”作為“等于”比較符。在許多高級(jí)語(yǔ)
11、言中,用“=”符號(hào)作為關(guān)系運(yùn)算符“等于”。例如,在BASIC或PASCAL程序中都可以寫if(a=b) then…但在C語(yǔ)言中,“=”是賦值運(yùn)算符,“==”才是關(guān)系運(yùn)算符“等于”。如果寫成if(a=b) printf("a equal to b");C編譯系統(tǒng)將(a=b)作為賦值表達(dá)式處理,將b的值賦給a,然后判斷a的值是否零,若為非零,則作為“真”;若為零作為假。如果a的值為3,b的值為4,,a≠b,按原意
12、不應(yīng)輸出“ae q u a lt ob”。而現(xiàn)在先將b的值賦給a,a也為4,賦值表達(dá)式的值為4。if語(yǔ)句中的表達(dá)式值為真(非零),因此輸出“ae q u a lt o b”。這種錯(cuò)誤在編譯時(shí)是檢查不出來的,但運(yùn)行結(jié)果往往是錯(cuò)的。而且由于習(xí)慣的影響,程序設(shè)計(jì)者自己往往也不易發(fā)覺。(7) 語(yǔ)句后面漏分號(hào)。C語(yǔ)言規(guī)定語(yǔ)句末尾必須有分號(hào)。分號(hào)是C語(yǔ)句不可缺少的一部分。這也是和其他語(yǔ)言不同的。有的初學(xué)者往往忘記寫這一分號(hào)。如:,a=3b=
13、4編譯時(shí),編譯程序在“a=3”后面未發(fā)現(xiàn)分號(hào),就把下一行“b=4”也作為上一行的語(yǔ)句的一部分,這就出現(xiàn)語(yǔ)法錯(cuò)誤。有時(shí)編譯時(shí)指出某行有錯(cuò),但在該行上并未發(fā)現(xiàn)錯(cuò)誤,應(yīng)該檢查上一行是否漏了分號(hào)。如果用復(fù)合語(yǔ)句,有的學(xué)過PASCAL語(yǔ)言的讀者往往漏寫最后一個(gè)語(yǔ)句的分號(hào),如:{t=a; a=b; b=t },在PASCAL中分號(hào)是兩個(gè)語(yǔ)句間的分隔符而不是語(yǔ)句的一部分,而在C中,沒有分號(hào)的就不是語(yǔ)句。(8) 在
14、不該加分號(hào)的地方加了分號(hào)。例如:if(a>b); printf("a is larger than b\n");本意為當(dāng)a>b時(shí)輸出“a is larger than b”的信息。但由于在if(a>b)后加了分號(hào),因此if語(yǔ)句到此結(jié)束。即當(dāng)(a>b)為真時(shí),執(zhí)行一個(gè)空語(yǔ)句。本來想a≤b時(shí)不輸出上述信息,但現(xiàn)在printf函數(shù)語(yǔ)句并不從屬于if語(yǔ)句,而是與if語(yǔ)句平行的語(yǔ)句。不論,a>b還是a≤b,都輸出
15、“a is larger than b”?! ∮秩纾骸 or(i=0;i<10;i++); {scanf("%d",&x); printf("%d\n",x*x); }本意為先后輸入10個(gè)數(shù),每輸入一個(gè)數(shù)后輸出它的平方值。由于在for( )后加了一個(gè)分號(hào),使循環(huán)體變成了空語(yǔ)句。只能輸入一個(gè)整數(shù)并輸出它的平方值??傊趇f、for、while語(yǔ)
16、句中,不要畫蛇添足多加分號(hào)。,(9) 對(duì)應(yīng)該有花括弧的復(fù)合語(yǔ)句,忘記加花括弧。如:sum=0; i=1; while(i<=100) sum=sum+i; i++;本意是實(shí)現(xiàn)1+2+…+100,即∑i。但上面的語(yǔ)句只是重復(fù)了sum+1的操作,而且循環(huán)永不終止。因?yàn)閕的值始終沒有改變。錯(cuò)誤在于沒有寫成復(fù)合語(yǔ)句形式。因此while語(yǔ)句的范圍到其后第一個(gè)分號(hào)為止。語(yǔ)句“i++;”不屬于循環(huán)體范圍之內(nèi)。
17、應(yīng)改,100i=0,為while(i<=100) {sum=sum+i; i++; }(10) 括弧不配對(duì)?! ‘?dāng)一個(gè)語(yǔ)句中使用多層括弧時(shí)常出現(xiàn)這類錯(cuò)誤,純屬粗心所致。如:while((c=getchar( )!='#')putchar(c);少了一個(gè)右括弧。,(11) 在用標(biāo)識(shí)符時(shí),忘記了大寫字母和小寫字母的區(qū)別?! ±纾簃ain( ) ?。鹖nt a,b,c;
18、 a=2;b=3; C=A+B; printf("%d+%d=%",A,B,C); ?。幾g時(shí)出錯(cuò)。編譯程序把a(bǔ)和A認(rèn)作是兩個(gè)不同的變量名處理,同樣b和B,c和C都分別代表兩個(gè)不同的變量。,(12) 引用數(shù)組元素時(shí)誤用了圓括弧。如:main( ) {int i,a(10); for(i=0;i<10;i++) scanf("%d&quo
19、t;,&a(i)); }C語(yǔ)言中對(duì)數(shù)組的定義或引用數(shù)組元素時(shí)必須用方括弧。(13) 在定義數(shù)組時(shí),將定義的“元素個(gè)數(shù)”誤認(rèn)為是“可使用的最大下標(biāo)值”。,main( ) ?。鹖nt a[10]={1,2,3,4,5,6,7,8,9,10}; int i; for(i=1;i<=10;i++)printf("%d",a[i]); }想輸出a[1]到a[10]。但一些初
20、學(xué)者常犯的錯(cuò)誤。C語(yǔ)言規(guī)定定義時(shí)用a[10],表示a數(shù)組有10個(gè)元素,而不是可以用的最大下標(biāo)值為10。數(shù)組只包括a[0]到a[9]10個(gè)元素,因此用a[10]就超出a數(shù)組的范圍了。,(14) 對(duì)二維或多維數(shù)組的定義和引用的方法不對(duì)。main( ) ?。鹖nt a[5,4];… printf("%d",a[1+2,2+2]);… }對(duì)二維數(shù)組和多維數(shù)組在定義和引用時(shí)必須將每一維的數(shù)據(jù)分別用方括
21、弧括起來。上面a[5,4]應(yīng)改為a[5][4],a[1+2,2+2]應(yīng)改為a[1+2][2+2]。根據(jù)C的語(yǔ)法規(guī)則,在一個(gè)方括弧中的是一個(gè)維的下標(biāo)表達(dá)式,a[1+2,2+2]中方括弧中的“1+2,2+2”,是一個(gè)逗號(hào)表達(dá)式,它的值是第二個(gè)數(shù)值表達(dá)式的值,即2+2的值為4。所以a[1+2,2+2]相當(dāng)于a[4]。而a[4]是a數(shù)組的第4行的首地址。因此執(zhí)行printf函數(shù)輸出的結(jié)果并不是a[3][4]的值,而是a數(shù)組第4行的首地址。(1
22、5) 誤以為數(shù)組名代表數(shù)組中全部元素。例如:main( ) {int a[4]={1,3,5,7}; printf("%d%d%d%d\n",a); ?。?企圖用數(shù)組名代表全部元素。在C語(yǔ)言中,數(shù)組名代表數(shù)組首地址,不能通過數(shù)組名輸出4個(gè)整數(shù)。(16) 混淆字符數(shù)組與字符指針的區(qū)別。main( ) ?。鹀har str[4]; str="Computer and c&
23、quot;; printf("%s\n",str); }編譯出錯(cuò)。str是數(shù)組名,代表數(shù)組首地址。在編譯時(shí)對(duì)str數(shù)組分配了一段內(nèi)存單元,因此在程序運(yùn)行期間str是一個(gè)常量,不能再被賦值。因此,,str=“Computer and c”是錯(cuò)誤的。如果把“char str[4];”改成“charstr;”,則程序正確。此時(shí)str是指向字符數(shù)據(jù)的指針變量,str=“Computer and c”是合
24、法的,它將字符串的首地址賦給指針變量str,然后在printf函數(shù)語(yǔ)句中輸出字符串“Computer and c”。因此應(yīng)當(dāng)弄清楚字符數(shù)組與字符指針變量用法的區(qū)別。(17) 在引用指針變量之前沒有對(duì)它賦予確定的值。main( ) ?。鹀har*p; scanf("%s",p);,… }沒有給指針變量p賦值就引用它,編譯時(shí)給出警告信息。應(yīng)當(dāng)改為charp,c[20]; p=c;
25、 scanf("%s",p);即先根據(jù)需要定義一個(gè)大小合適的字符數(shù)組c,然后將c數(shù)組的首地址賦給指針變量p,此時(shí)p有確定的值,指向數(shù)組c。再執(zhí)行scanf函數(shù)就沒有問題了,把從鍵盤輸入的字符串存放到字符數(shù)組c中。(18) switch語(yǔ)句的各分支中漏寫break語(yǔ)句。,例如:switch(score) ?。鹀ase 5:printf("Very good!"); case 4:
26、printf("Good!"); case 3:printf("Pass!"); case 2:printf("Fail!"); defult:printf("data error!"); }上述switch語(yǔ)句的作用是希望根據(jù)score(成績(jī))打印出評(píng)語(yǔ)。但當(dāng)score的值為5時(shí),輸出為Very Good!Goo
27、d!Pass!Fail!data error!,原因是漏寫了break語(yǔ)句。case只起標(biāo)號(hào)的作用,而不起判斷作用,因此在執(zhí)行完第一個(gè)printf函數(shù)語(yǔ)句后接著執(zhí)行第2、3、4、5個(gè)printf函數(shù)語(yǔ)句。應(yīng)改為switch(score) {case 5:printf("Verygood!");break; case 4:printf("Good!"); break;
28、 case 3:printf("Pass!"); break; case 2:print("Fail!"); break; defult:print("data error!");,}(19) 混淆字符和字符串的表示形式。char sex; sex="M";…sex是字符變量,只能存放一個(gè)字符。而字符
29、常量的形式是用單引號(hào)括起來的,應(yīng)改為sex='M';“M”是用雙引號(hào)括起來的字符串,它包括兩個(gè)字符:‘M’和‘\0’,無法存放到字符變量sex中。,(20) 使用自加(++)和自減(--)運(yùn)算符時(shí)出的錯(cuò)誤?! ±纾簃ain( ) ?。鹖ntp,a[5]={1,3,5,7,9}; p=a; printf("%d",*p++); ?。簧偃苏J(rèn)為“*p++”的作用是
30、先使p加1,即指向第1個(gè)元素a[1]處,然后輸出第一個(gè)元素a[1]的值3。其實(shí)應(yīng)該是先執(zhí)行p++,而p++的作用是先用p的原值,用完后使p加1。p的原值指向數(shù)組a的第0個(gè)元素a[0],,因此*p就是第0個(gè)元素a[0]的值1。結(jié)論是先輸出a[0]的值,然后再使p加1。如果是*(++p),則先使p指向a[1],然后輸出a[1]的值。(21) 有人習(xí)慣用傳統(tǒng)的方式對(duì)函數(shù)形參進(jìn)行聲明,但卻把對(duì)函數(shù)的形參和函數(shù)中的局部變量混在一起定義。如:m
31、ax(x,y) int x,y,z; ?。?x>y?x,y; return(z); },應(yīng)改為max(x,y) int x,y; {int z; z=x>y?x:y; return(z); }(22) 所調(diào)用的函數(shù)在調(diào)用語(yǔ)句之后才定義,而又在調(diào)用前未加說明。main( ) ?。鹒loat x,y,z;,x=3.5;y=-7.6; z=max(x,y);
32、 printf("%f\n",z); } float max(float x,float y) {return(z=x>y?x:y);}這個(gè)程序乍看起來沒有什么問題,但在編譯時(shí)有出錯(cuò)信息。原因是max函數(shù)是實(shí)型的,而且在main函數(shù)之后才定義,也就是max函數(shù)的定義位置在main函數(shù)中的調(diào)用max函數(shù)之后。改錯(cuò)的方法可以用以下二者之一:,① 在main函數(shù)中增加一個(gè)對(duì)max函數(shù)的聲明,即
33、函數(shù)的原型:main( ) {float max(float,float);/*聲明將要用到的max函數(shù)為實(shí)型*/ float x,y,z; x=3.5;y=-7.6; z=max(x,y); printf("%f\n",z); ?。?將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即:,float max(float x,float y) {re
34、turn(z=x>y?x:y);} main() ?。鹒loat x,y,z; x=3.5;y=-7.6; z=max(x,y); printf("%f\n",z); }這樣,編譯時(shí)不會(huì)出錯(cuò),程序運(yùn)行結(jié)果是正確的。,(23) 誤認(rèn)為形參值的改變會(huì)影響實(shí)參的值。main( ) ?。鹖nta,b; a=3;b=4; swap(a,b);
35、 printf("%d,%d\n",a,b); ?。 wap(int x,int y) ?。鹖nt t; t=x;x=y;y=t; },原意是通過調(diào)用swap函數(shù)使a和b的值對(duì)換,然后在main函數(shù)中輸出已對(duì)換了值的a和b。但是這樣的程序是達(dá)不到目的的,因?yàn)閤和y的值的變化是不傳送回實(shí)參a和b的,main函數(shù)中的a和b的值并未改變。如果想從函數(shù)得到一個(gè)以上的變化了的值,應(yīng)該用指針
36、變量。用指針變量作函數(shù)參數(shù),使指針變量所指向的變量的值發(fā)生變化。此時(shí)變量的值改變了,主調(diào)函數(shù)中可以利用這些已改變的值。如:main( ) ?。鹖nt a,b,*p1,*p2; a=3;b=4;,p1=&a;p2=&b; swap(p1,p2); printf("%d,%d\n",a,b); /a和b的值已對(duì)換/ } swap(int *pt1, int
37、 *pt2) ?。鹖nt t; t=*pt1;*pt1=*pt2;*pt2=t; }(24) 函數(shù)的實(shí)參和形參類型不一致。main( ),{int a=3,b=4; c=fun(a,b);… } fun(float x,float y) {… }實(shí)參a、b為整型,形參x、y為實(shí)型。a和b的值傳遞給x和y時(shí),x和y的值并非3和4。C要求實(shí)參與形參的類型一致。如
38、果在main函數(shù)中對(duì)fun作原型,聲明:fun (float, float);程序可以正常運(yùn)行,此時(shí),按不同類型間的賦值的規(guī)則處理,在虛實(shí)結(jié)合后x=3.0, y=4.0。也可以將fun函數(shù)的位置調(diào)到main函數(shù)之前,也可獲正確結(jié)果。(25) 不同類型的指針混用。main( ) ?。鹖nt i=3,*p1; float a=1.5,*p2; p1=&i; p2=&a;,p2=p1;
39、 printf("%d,%d\n",*p1,*p2); ?。髨D使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指向整型變量。指向不同類型的指針間的賦值必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換。如:p2=(float*)p1;作用是先將p1的值轉(zhuǎn)換成指向?qū)嵭偷闹羔?,然后再賦給p2。這種情況在C程序中是常見的。例如,用malloc函數(shù)開辟內(nèi)存單元,函數(shù)返回的是指向被分配內(nèi)存空間的void *類型的指針。而人們希望開辟的是,
40、存放一個(gè)結(jié)構(gòu)體變量值的存儲(chǔ)單元,要求得到指向該結(jié)構(gòu)體變量的指針,可以進(jìn)行如下的類型轉(zhuǎn)換。struct student ?。鹖nt num; char name[20]; float score; }; struct student student1,*p;… p=(struct student *)malloc(LEN);,p是指向struct student結(jié)構(gòu)體類型數(shù)據(jù)的指針,將mal
41、loc函數(shù)返回的void *類型指針轉(zhuǎn)換成指向struct student類型變量的指針。(26) 沒有注意函數(shù)參數(shù)的求值順序。例如有以下語(yǔ)句:i=3;printf("%d,%d,%d\n",i,++i,++i); 許多人認(rèn)為輸出必然是3,4,5實(shí)際不盡然。在Turbo C和其他一些C系統(tǒng)中輸出是5,5,4,因?yàn)檫@些系統(tǒng)是采取自右至左的順序求函數(shù)參數(shù)的值。先求出最右面一個(gè)參數(shù)(++i)的值為4,再求出第
42、2個(gè)參數(shù)(++i)的值為5,最后求出最左面的參數(shù)(i)的值5。C標(biāo)準(zhǔn)沒有具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而左。但每個(gè)C編譯程序都有自己的順序,在有些情況下,從左到右求解和從右到左求解的結(jié)果是相同的。例如fun1(a+b,b+c,c+a);fun1是一個(gè)函數(shù)名。3個(gè)實(shí)參表達(dá)式a+b、b+c、c+a。在一般情況下,自左至右地求這3個(gè)表達(dá)式的值和自右至左地求它們的值是一樣的,但在前面舉的例子,是不相同的。因此,建議最好不用會(huì)
43、引起二義性的用法。如果在上例中,希望輸出“3,4,5”時(shí),可以改用i=3; j=i+1; k=j+1;printf("%d,%d,%d\n",i,j,k);(27) 混淆數(shù)組名與指針變量的區(qū)別。main( ) ?。鹖nt i,a[5]; for(i=0;i<5;i++)scanf("%d",a++);…,}企圖通過a的改變使指針下移,每次指向欲輸入數(shù)據(jù)的數(shù)組元素。它的錯(cuò)
44、誤在于不了解數(shù)組名代表數(shù)組首地址,它的值是不能改變的,用a++是錯(cuò)誤的,應(yīng)當(dāng)用指針變量來指向各數(shù)組元素。即:int i,a[5],*p;p=a;for(i=0;i<5;i++)scanf("%d",p++); 或int a[5],*p;for(p=a;p<a+5;p++),scanf("%d",p); (28) 混淆結(jié)構(gòu)體類型與結(jié)構(gòu)體變量的區(qū)別,對(duì)一個(gè)結(jié)構(gòu)體類型賦值。struc
45、t worker ?。鹟ong int num; char name[20]; char sex; int age; }; worker.num=187045;,strcpy(worker.name,"ZhangFun"); worker.sex='M'; worker.a(chǎn)ge=18; 這是錯(cuò)誤的,只能對(duì)變量賦值而不能對(duì)
46、類型賦值。上面只定義了struct worker類型而未定義變量。應(yīng)改為struct worker ?。鹟ong int num; char name[20]; char sex; int age;,}; struct worker worker-1; worker-1.num=187045; strcpy(worker-1.name,"Zhang Fun&q
47、uot;); worker-1.sex='M'; worker-1.a(chǎn)ge=18; 今定義了結(jié)構(gòu)體變量worker-1,并對(duì)其中的各成員賦值。(29) 使用文件時(shí)忘記打開,或打開方式與使用情況不匹配。例如,對(duì)文件的讀寫,用只讀方式打開,卻企圖向,該文件輸出數(shù)據(jù),例如:if((fp=fopen("test","r"))==NULL) {printf(&q
48、uot;cannot open this file\n"); exit(0); } ch=fgetc(fp); while(ch!='#') {ch=ch+4; fputc(ch,fp); ch=fget(fp); },對(duì)以“r”方式(只讀方式)打開的文件,進(jìn)行既讀又寫的操作,顯然是不行的。此外,有的程序常
49、忘記關(guān)閉文件,雖然系統(tǒng)會(huì)自動(dòng)關(guān)閉所用文件,但可能會(huì)丟失數(shù)據(jù)。因此必須在用完文件后關(guān)閉它。以上只是列舉了一些初學(xué)者常出現(xiàn)的錯(cuò)誤,這些錯(cuò)誤大多是對(duì)于C語(yǔ)法不熟悉之故。對(duì)C語(yǔ)言使用多了,比較熟練了,犯這些錯(cuò)誤自然就會(huì)減少了。在深入使用C語(yǔ)言后,還會(huì)出現(xiàn)其他一些更深入、更隱蔽的錯(cuò)誤。,程序出錯(cuò)有三種情況:① 語(yǔ)法錯(cuò)誤。指違背了C語(yǔ)法的規(guī)定,對(duì)這類錯(cuò)誤,編譯程序一般能給出“出錯(cuò)信息”,并且告訴你在哪一行出錯(cuò)。只要細(xì)心,是可以很快發(fā)現(xiàn)并排除的。
50、② 邏輯錯(cuò)誤。程序并無違背語(yǔ)法規(guī)則,但程序執(zhí)行結(jié)果與原意不符。這是由于程序設(shè)計(jì)人員設(shè)計(jì)的算法有錯(cuò)或編寫程序有錯(cuò),通知給系統(tǒng)的指令與解題的原意不相同,即出現(xiàn)了邏輯上的混亂。例如:前面第9條錯(cuò)誤:sum=0;i=1; while(i<=100) sum=sum+i;,i++;語(yǔ)法并無錯(cuò)誤。但while語(yǔ)句通知給系統(tǒng)的信息是當(dāng)i≤100時(shí),執(zhí)行“sum=sum+i;”。C系統(tǒng)無法辨別程序中這個(gè)語(yǔ)句是否符合作者的原意,而
51、只能忠實(shí)地執(zhí)行這一指令。這種錯(cuò)誤比語(yǔ)法錯(cuò)誤更難檢查。要求程序員有較豐富的經(jīng)驗(yàn)。③ 運(yùn)行錯(cuò)誤。程序既無語(yǔ)法錯(cuò)誤,也無邏輯錯(cuò)誤,但在運(yùn)行時(shí)出現(xiàn)錯(cuò)誤甚至停止運(yùn)行。例如:int a ,b ,c;scanf("%d %d",&a,&b);c=b/a;printf("c=%d\n",c);,輸入a和b的值, 輸出b/a的值, 程序沒有錯(cuò)。 但是如果輸入a的值為0, 就會(huì)出現(xiàn)錯(cuò)誤。 因
52、此程序應(yīng)能適應(yīng)不同的數(shù)據(jù), 或者說能經(jīng)受各種數(shù)據(jù)的“考驗(yàn)” , 具有“健壯性”。寫完一個(gè)程序只能說完成任務(wù)的一半(甚至不到一半)。調(diào)試程序往往比寫程序更難,更需要精力、時(shí)間和經(jīng)驗(yàn)。常常有這樣的情況:程序花一天就寫完了,而調(diào)試程序二三天也未能完。有時(shí)一個(gè)小小的程序會(huì)出錯(cuò)五六處,而發(fā)現(xiàn)和排除一個(gè)錯(cuò)誤,有時(shí)竟需要半天,甚至更多。希望讀者通過實(shí)踐掌握調(diào)試程序的方法和技術(shù)。,16.2 程 序 調(diào) 試所謂程序調(diào)試是指對(duì)程序的查錯(cuò)和排錯(cuò)。調(diào)試程
53、序一般應(yīng)經(jīng)過以下幾個(gè)步驟:(1) 先進(jìn)行人工檢查,即靜態(tài)檢查。在寫好一個(gè)程序以后,不要匆匆忙忙上機(jī),而應(yīng)對(duì)紙面上的程序進(jìn)行人工檢查。這一步是十分重要的,它能發(fā)現(xiàn)程序設(shè)計(jì)人員由于疏忽而造成的多數(shù)錯(cuò)誤。而這一步驟往往容易被人忽視。有人總希望把一切推給計(jì)算機(jī)系統(tǒng)去做,但這樣就會(huì)多占用機(jī)器時(shí)間。而且,作為一個(gè)程序人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)?shù)目茖W(xué)作風(fēng),每一步都要嚴(yán)格把關(guān),不把問題留給后面的工序?! 榱烁行У剡M(jìn)行人工檢查,所編的程序應(yīng)注意力,求做到以下
54、幾點(diǎn):①應(yīng)當(dāng)采用結(jié)構(gòu)化程序方法編程,以增加可讀性;②盡可能多加注釋,以幫助理解每段程序的作用;③在編寫復(fù)雜的程序時(shí),不要將全部語(yǔ)句都寫在main函數(shù)中,而要多利用函數(shù),用一個(gè)函數(shù)來實(shí)現(xiàn)一個(gè)單獨(dú)的功能。這樣既易于閱讀也便于調(diào)試,各函數(shù)之間除用參數(shù)傳遞數(shù)據(jù)這一渠道以外,數(shù)據(jù)間盡量少出現(xiàn)耦合關(guān)系,便于分別檢查和處理。(2) 在人工(靜態(tài))檢查無誤后,才可以上機(jī)調(diào)試。通過上機(jī)發(fā)現(xiàn)錯(cuò)誤稱動(dòng)態(tài)檢查。在編譯時(shí)給出語(yǔ)法錯(cuò)誤的信息(包括哪一行有錯(cuò)以及錯(cuò)
55、誤類型),可以根據(jù)提示的信息具體找出程序中出錯(cuò)之處并改正之。應(yīng)當(dāng)注意的是:有時(shí)提示的出錯(cuò)行并不是真正出錯(cuò),的行,如果在提示出錯(cuò)的行上找不到錯(cuò)誤的話應(yīng)當(dāng)?shù)缴弦恍性僬?。另外,有時(shí)提示出錯(cuò)的類型并非絕對(duì)準(zhǔn)確,由于出錯(cuò)的情況繁多而且各種錯(cuò)誤互有關(guān)聯(lián),因此要善于分析,找出真正的錯(cuò)誤,而不要只從字面意義上死摳出錯(cuò)信息,鉆牛角尖。如果系統(tǒng)提示的出錯(cuò)信息多,應(yīng)當(dāng)從上到下逐一改正。有時(shí)顯示出一大片出錯(cuò)信息往往使人感到問題嚴(yán)重,無從下手。其實(shí)可能只有一二
56、個(gè)錯(cuò)誤。例如,對(duì)所用的變量未定義,編譯時(shí)就會(huì)對(duì)所有含該變量的語(yǔ)句發(fā)出出錯(cuò)信息。只要加上一個(gè)變量定義,所有錯(cuò)誤都消除了。(3) 在改正語(yǔ)法錯(cuò)誤(包括“錯(cuò)誤”(error)和“警告”(warning))后,程序經(jīng)過連接(link)就得到可,執(zhí)行的目標(biāo)程序。運(yùn)行程序,輸入程序所需數(shù)據(jù),就可得到運(yùn)行結(jié)果。應(yīng)當(dāng)對(duì)運(yùn)行結(jié)果作分析,看它是否符合要求。有的初學(xué)者看到輸出運(yùn)行結(jié)果就認(rèn)為沒問題了,不作認(rèn)真分析,這是危險(xiǎn)的。有時(shí),數(shù)據(jù)比較復(fù)雜,難以立即判
57、斷結(jié)果是否正確??梢允孪瓤紤]好一批“試驗(yàn)數(shù)據(jù)”,輸入這些數(shù)據(jù)可以得出容易判斷正確與否的結(jié)果。例如解方程ax2+bx+c=0,輸入a、b、c的值分別為1、-2、1時(shí),根x的值是1。這是容易判斷的,若根不等于1,程序顯然有錯(cuò)。但是,用“試驗(yàn)數(shù)據(jù)”時(shí),程序運(yùn)行結(jié)果正確,還不能保證程序完全正確。因?yàn)橛锌赡茌斎肓硪唤M數(shù)據(jù)時(shí)運(yùn)行結(jié)果不對(duì)。例如,用x=-b±b2-4ac2a公式,求根x的值,當(dāng)a≠0和b2-4ac>0時(shí),能得出正確結(jié)果,當(dāng)
58、a=0或b2-4ac<0時(shí),就得不到正確結(jié)果(假設(shè)程序中未對(duì)a=0作防御處理以及未作復(fù)數(shù)處理)。因此應(yīng)當(dāng)把程序可能遇到的多種方案都一一試到。例如,if語(yǔ)句有兩個(gè)分支,有可能在流程經(jīng)過其中一個(gè)分支時(shí)結(jié)果正確,而經(jīng)過另一個(gè)分支時(shí)結(jié)果不對(duì)。必須考慮周全。事實(shí)上,當(dāng)程序復(fù)雜時(shí)很難把所有的可能方案全部都試到,選擇典型的情況作試驗(yàn)即可。(4) 運(yùn)行結(jié)果不對(duì),大多屬于邏輯錯(cuò)誤。對(duì)這類錯(cuò)誤往往需要仔細(xì)檢查和分析才能發(fā)現(xiàn)??梢圆捎靡韵罗k法:,① 將程
59、序與流程圖(或偽代碼)仔細(xì)對(duì)照,如果流程圖是正確的話,程序?qū)戝e(cuò)了,是很容易發(fā)現(xiàn)的。例如,復(fù)合語(yǔ)句忘記寫花括弧,只要一對(duì)照流程圖就能很快發(fā)現(xiàn)。② 如果實(shí)在找不到錯(cuò)誤,可以采取“分段檢查”的方法。在程序不同位置設(shè)幾個(gè)printf函數(shù)語(yǔ)句,輸出有關(guān)變量的值,逐段往下檢查。直到找到在某一段中數(shù)據(jù)不對(duì)為止。這時(shí)就已經(jīng)把錯(cuò)誤局限在這一段中了。不斷縮小“查錯(cuò)區(qū)”,就可能發(fā)現(xiàn)錯(cuò)誤所在。③ 也可以用第9章介紹過的“條件編譯”命令進(jìn)行程序調(diào)試(在程序調(diào)
60、試階段,若干printf函數(shù)語(yǔ)句要進(jìn)行編譯并執(zhí)行。當(dāng)調(diào)試完畢,這些語(yǔ)句不要再,編譯了,也不再被執(zhí)行了)。這種方法可以不必一一刪去printf函數(shù)語(yǔ)句,以提高效率。④ 如果在程序中沒有發(fā)現(xiàn)問題,就要檢查流程圖有無錯(cuò)誤,即算法有無問題,如有則改正之,接著修改程序。⑤ 有的系統(tǒng)還提供debug(調(diào)試)工具,跟蹤流程并給出相應(yīng)信息,使用更為方便,請(qǐng)查閱有關(guān)手冊(cè)??傊?,程序調(diào)試是一項(xiàng)細(xì)致深入的工作,需要下功夫、動(dòng)腦子、善于累積經(jīng)驗(yàn)。在程序調(diào)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫(kù)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 異常處理和程序調(diào)試
- hypermesh常見錯(cuò)誤和知識(shí)集錦
- srio調(diào)試過程和錯(cuò)誤的基本判決
- 常見錯(cuò)誤示例和常見問題解答
- 常見錯(cuò)誤示例和常見問題解答
- 常見錯(cuò)誤示例和常見問題解答
- c++程序中的常見錯(cuò)誤和解決方法
- 估價(jià)常見錯(cuò)誤
- 常見診療錯(cuò)誤
- 3vb常見錯(cuò)誤和難點(diǎn)分析
- vc程序調(diào)試
- 程序調(diào)試方法
- 常見中式英文錯(cuò)誤
- 常見編譯錯(cuò)誤
- 公文寫作常見錯(cuò)誤
- 公文常見錯(cuò)誤格式
- quartus常見錯(cuò)誤分析
- c語(yǔ)言程序編寫和調(diào)試環(huán)境簡(jiǎn)介
- 常見英文用法錯(cuò)誤
- quartus常見錯(cuò)誤分析
評(píng)論
0/150
提交評(píng)論