C語言程序設(shè)計(jì)實(shí)例教程(第2版)第13章程序調(diào)試與常見錯(cuò)誤分析課件_第1頁
C語言程序設(shè)計(jì)實(shí)例教程(第2版)第13章程序調(diào)試與常見錯(cuò)誤分析課件_第2頁
C語言程序設(shè)計(jì)實(shí)例教程(第2版)第13章程序調(diào)試與常見錯(cuò)誤分析課件_第3頁
C語言程序設(shè)計(jì)實(shí)例教程(第2版)第13章程序調(diào)試與常見錯(cuò)誤分析課件_第4頁
C語言程序設(shè)計(jì)實(shí)例教程(第2版)第13章程序調(diào)試與常見錯(cuò)誤分析課件_第5頁
已閱讀5頁,還剩117頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第13章程序調(diào)試與常見錯(cuò)誤分析13.1程序調(diào)試13.2常見錯(cuò)誤分析第13章程序調(diào)試與常見錯(cuò)誤分析13.1程序調(diào)試113.1程序調(diào)試 13.1.1程序調(diào)試的步驟 所謂程序調(diào)試,是指對(duì)程序的查錯(cuò)和排錯(cuò)。調(diào)試程序一般應(yīng)經(jīng)過以下幾個(gè)步驟。 1.人工檢查,即靜態(tài)檢查 在寫好一個(gè)程序以后,不要匆匆忙忙上機(jī),而應(yīng)對(duì)紙面上的程序進(jìn)行人工檢查。這一步是十分重要的,它能發(fā)現(xiàn)程序設(shè)計(jì)人員由于疏忽而造成的多處錯(cuò)誤。而這一步驟往往容易被忽視。有的用戶總希望把一切推給計(jì)算機(jī)系統(tǒng)去做,但這樣就會(huì)多占用機(jī)器時(shí)間。13.1程序調(diào)試 13.1.1程序調(diào)試的步驟2 而且,作為一個(gè)程序設(shè)計(jì)人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)科學(xué)的作風(fēng),每一步都要嚴(yán)格把關(guān),不要把問題留給后面的工序。 為了更有效地進(jìn)行人工檢查,所編的程序應(yīng)力求做到以下幾點(diǎn): (1)應(yīng)當(dāng)采用結(jié)構(gòu)化程序方法編程,以增加可讀性。 (2)盡可能多地加注釋,以幫助理解每段程序的作用。 (3)在編寫復(fù)雜的程序時(shí),不要將全部語句都寫在main函數(shù)中,而要多利用函數(shù),用一個(gè)函數(shù)來實(shí)現(xiàn)一個(gè)單獨(dú)的功能。這樣既易于閱讀,也便于調(diào)試,各函數(shù)之間除用參數(shù)傳遞數(shù)據(jù)外,數(shù)據(jù)間應(yīng)盡量少出現(xiàn)耦合關(guān)系,以便于分別檢查和處理。 而且,作為一個(gè)程序設(shè)計(jì)人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)科學(xué)的作風(fēng),每一步3 2.上機(jī)調(diào)試,即動(dòng)態(tài)檢查 在人工(靜態(tài))檢查無誤后,才可以上機(jī)調(diào)試。通過上機(jī)發(fā)現(xiàn)的錯(cuò)誤稱之為動(dòng)態(tài)檢查。在編譯時(shí),系統(tǒng)會(huì)給出語法錯(cuò)誤的信息(包括哪一行有錯(cuò)以及錯(cuò)誤類型),用戶可以根據(jù)提示的信息具體找出程序中出錯(cuò)之處并進(jìn)行修改。應(yīng)當(dāng)注意的是:有時(shí)提示的出錯(cuò)行并不是真正出錯(cuò)的行,如果在提示出錯(cuò)的行上找不到錯(cuò)誤的話,應(yīng)當(dāng)?shù)缴弦恍性僬摇A硗?,有時(shí)提示出錯(cuò)的類型并非絕對(duì)準(zhǔn)確,由于出錯(cuò)的情況繁多而且各種錯(cuò)誤互有關(guān)聯(lián),因此要善于分析,找出真正的錯(cuò)誤,而不要死抱住提示的出錯(cuò)信息不放,鉆牛角尖。 2.上機(jī)調(diào)試,即動(dòng)態(tài)檢查4 如果系統(tǒng)提示的出錯(cuò)信息多,應(yīng)當(dāng)從上到下逐一改正。有時(shí)顯示出一大片錯(cuò)誤信息往往使人感到問題嚴(yán)重,無從下手。其實(shí)可能只有一兩個(gè)錯(cuò)誤。例如,對(duì)所用的變量未定義,編譯時(shí)就會(huì)對(duì)所有含該變量的語句發(fā)出出錯(cuò)信息。這時(shí)只要加上一個(gè)變量定義,那么所有錯(cuò)誤就都消除了。 3.運(yùn)行程序,試驗(yàn)數(shù)據(jù) 在改正語法錯(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)的。 如果系統(tǒng)提示的出錯(cuò)信息多,應(yīng)當(dāng)從上到下逐一改正。有時(shí)顯示5 有時(shí),數(shù)據(jù)比較復(fù)雜,難以立即判斷結(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ì)。例如,用 有時(shí),數(shù)據(jù)比較復(fù)雜,難以立即判斷結(jié)果是否正確。可以事先6 公式求根x的值,當(dāng)a0和b2-4ac>0時(shí),能得出正確結(jié)果;當(dāng)a=0或b2-4ac<0時(shí),就得不到正確結(jié)果(假設(shè)程序中未對(duì)a=0作防御處理以及未作復(fù)數(shù)處理)。因此,應(yīng)當(dāng)把程序可能遇到的多種方案都一一試到。再如,if語句有兩個(gè)分支,有可能在流程經(jīng)過其中一個(gè)分支的結(jié)果正確,而經(jīng)過另一個(gè)分支時(shí)結(jié)果不正確。這些都必須考慮周全。 13.1.2檢查和分析錯(cuò)誤原因 運(yùn)行結(jié)果不對(duì),大多屬于邏輯錯(cuò)誤。對(duì)這類錯(cuò)誤往往需要仔細(xì)檢查和分析才能發(fā)現(xiàn),可以采用的方法有以下幾種:

公式求根x的值,當(dāng)a0和b2-4ac>0時(shí),能得出正確7 (1)如果在程序中沒有發(fā)現(xiàn)問題,可以檢查流程圖有無錯(cuò)誤,即算法有無問題,如有,就需改正,接著修改程序。將程序與流程圖(或偽代碼)仔細(xì)對(duì)照,如果流程圖是正確的話,則是程序?qū)戝e(cuò)了,這很容易被發(fā)現(xiàn)的。例如,復(fù)合語句忘記寫花括弧“{}”,只要對(duì)照流程圖很快就能發(fā)現(xiàn)。 (2)如果實(shí)在找不到錯(cuò)誤,可以采取“分段檢查”的方法。在程序不同位置設(shè)幾個(gè)printf函數(shù)語句,輸出有關(guān)變量的值,逐段往下檢查,直到找到在某一段中數(shù)據(jù)不對(duì)的段為止。這時(shí)就已經(jīng)把錯(cuò)誤局限在這一段中了。不斷縮小“查錯(cuò)區(qū)”,就可能發(fā)現(xiàn)錯(cuò)誤所在。 (1)如果在程序中沒有發(fā)現(xiàn)問題,可以檢查流程圖有無錯(cuò)誤8 (3)也可以用第9章介紹過的“條件編譯”命令來進(jìn)行程序調(diào)試。在程序調(diào)試階段,若干printf函數(shù)語句要進(jìn)行編譯并執(zhí)行。當(dāng)調(diào)試完畢時(shí),這些語句不再編譯,也不再被執(zhí)行了。這種方法可以不必一一刪除printf函數(shù)語句,因此可以提高效率。 (4)有的系統(tǒng)還提供debug(調(diào)試)工具,跟蹤流程并給出相應(yīng)信息,使用更為方便,相關(guān)內(nèi)容請(qǐng)查閱有關(guān)手冊(cè)。 總之,程序調(diào)試是一項(xiàng)細(xì)致深入的工作,需要下功夫、動(dòng)腦子、善于累積經(jīng)驗(yàn)。在程序調(diào)試過程中往往反映出一個(gè)人的水平、經(jīng)驗(yàn)和科學(xué)態(tài)度。上機(jī)調(diào)試程序的目的決不是為了“驗(yàn)證程序的正確性”,而是“掌握調(diào)試方法和技術(shù)”。 (3)也可以用第9章介紹過的“條件編譯”命令來913.2常見錯(cuò)誤分析 下面列舉出初學(xué)者易犯的錯(cuò)誤,以提醒讀者注意。 (1)誤把“=”作為“等于”比較符。 C語言中,“=”是賦值運(yùn)算符,“==”才是關(guān)系運(yùn)算符“等于”。如果寫成 if(a=b)printf("aequaltob");

13.2常見錯(cuò)誤分析 下面列舉出初學(xué)者易犯的錯(cuò)誤,以提10 C編譯程序會(huì)將(a=b)作為賦值表達(dá)式處理,將b的值賦給a,然后判斷a的值是否為0,若為非0,則作為“真”;若為0,則作為“假”。如果a的值為3,b的值為4,a≠b,按原意不應(yīng)輸出“aequaltob”。而現(xiàn)在先將b的值賦給a,a也為4,賦值表達(dá)式的值為4。if語句中的表達(dá)式值為“真”(非0),因此輸出“aequaltob”。 這種錯(cuò)誤在編譯時(shí)是檢查不出來的,但運(yùn)行結(jié)果往往是錯(cuò)的,而且,由于習(xí)慣的影響,程序設(shè)計(jì)者自己往往也不易發(fā)覺。 C編譯程序會(huì)將(a=b)作為賦值表達(dá)式處理,將b的值賦給11 (2)使用自加(++)和自減(--)運(yùn)算符時(shí)出錯(cuò)。例如:main(){int*p,a[5]={1,3,5,7,9};p=a;printf("%d",*p++);} 不少人認(rèn)為“*p++”的作用是先使p加1,即指向第1個(gè)元素a[1]處,然后輸出第一個(gè)元素a[1]的值3。其實(shí)應(yīng)該是,先執(zhí)行p++,而*p就是第0個(gè)元素a[0]的值1。結(jié)論是,先輸出a[0]的值,然后再使p加1。如果是*(++p),則先使p指向a[1],然后輸出a[1]的值。 (2)使用自加(++)和自減(--)運(yùn)算符時(shí)出錯(cuò)。12 (3)混淆字符和字符串的表示形式。例如:charsex;sex="M";

sex是字符變量,只能存放一個(gè)字符。而字符常量的形式是用單引號(hào)括起來的,應(yīng)改為 sex='M'; "M"是用雙引號(hào)括起來的字符串,它包括兩個(gè)字符M和\0,無法存放到字符變量sex中。 (3)混淆字符和字符串的表示形式。13 (4)語句后面漏分號(hào)。 C語言規(guī)定語句末尾必須有分號(hào)。分號(hào)是C語句中不可缺少的一部分。這也是和其他語言的不同之處。有的初學(xué)者往往忘記寫這一分號(hào)。如:a=3b=4 編譯時(shí),編譯程序在a=3后面未發(fā)現(xiàn)分號(hào),就把下一行b=4也作為上一行的語句的一部分,這樣就會(huì)出現(xiàn)語法錯(cuò)誤。有時(shí)編譯時(shí)指出某行有錯(cuò),但在該行上并未發(fā)現(xiàn)錯(cuò)誤,就應(yīng)該檢查上一行是否漏了分號(hào)。 (4)語句后面漏分號(hào)。14 (5)輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。 例如,若a已定義為整型,b已定義為實(shí)型。 a=3;b=4.5; printf("%f%d\n",a,b); 編譯時(shí)不給出出錯(cuò)信息,但運(yùn)行結(jié)果將與原意不符,輸出為 0.00000016402 它們并不是按照賦值的規(guī)則進(jìn)行轉(zhuǎn)換(如把4.5轉(zhuǎn)換成4),而是將數(shù)據(jù)在存儲(chǔ)單元中的形式按格式符的要求組織輸出(如b占4個(gè)字節(jié),只把最后兩個(gè)字節(jié)中的數(shù)據(jù)按%d作為整數(shù)輸出)。 (5)輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。15 (6)忘記定義變量。例如:main(){x=3;y=6;printf("%d\n",x+y);} C語言要求對(duì)程序中用到的每一個(gè)變量都必須定義其類型,上面程序中沒有對(duì)x、y進(jìn)行定義。應(yīng)在函數(shù)體的開頭加上下面的語句。 intx,y; (6)忘記定義變量。16 (7)未注意int型數(shù)據(jù)的數(shù)值范圍。 一般微型計(jì)算機(jī)上使用的C語言編譯版本,為一個(gè)整型數(shù)據(jù)分配2個(gè)字節(jié)。因此,一個(gè)整數(shù)的范圍為-215~215-1,即-32768~32767。常見這樣的程序段:intnum;num=89101;printf("%d",num); 得到的卻是23565,原因是89101已超過整數(shù)所要求的范圍32767。2個(gè)字節(jié)容納不下89101,則將高位截去。 (7)未注意int型數(shù)據(jù)的數(shù)值范圍。17 (8)輸入時(shí)數(shù)據(jù)的組織與要求不符。 用scanf函數(shù)輸入數(shù)據(jù)時(shí),應(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)該用以下方法輸入。

(8)輸入時(shí)數(shù)據(jù)的組織與要求不符。18 34如果scanf函數(shù)為scanf("%d,%d",&a,&b); 對(duì)scanf函數(shù)中格式字符串中除了格式說明符外,對(duì)其他字符必須按原樣輸入。因此,應(yīng)按以下方法輸入。 3,4 此時(shí)如果用“34”反而錯(cuò)了。還應(yīng)注意,不能企圖用 scanf("inputa&b:%d,%d",&a,&b); 在屏幕上顯示一行信息: inputa&b: 3419 然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以為scanf具有BASIC語言中的INPUT語句的功能(先輸出一個(gè)字符串,再輸入變量的值)。如果想在屏幕上得到所需的提示信息,可以另加一個(gè)printf函數(shù)語句。printdf("inputa&b:");scanf("%d,%d",&a,&b); 然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以20 (9)在不該加分號(hào)的地方加了分號(hào)。 例如:if(a>b);printf("aislargerthanb\n"); 本意為,當(dāng)a>b時(shí)輸出“aislargerthanb”的信息。但由于在“if(a>b)”后加了分號(hào),因此if語句到分號(hào)結(jié)束。即當(dāng)(a>b)為真時(shí),執(zhí)行一個(gè)空語句。本來想a≤b時(shí)不輸出上述信息,但現(xiàn)在printf函數(shù)語句并不從屬于if語句,而是與if語句平行的語句。不論a>b還是 a≤b,“aislargerthanb”都被輸出出來。 (9)在不該加分號(hào)的地方加了分號(hào)。21 又如:for(i=0;i<10;i++);{scanf("%d",&x);printf("%d\n",x*x);} 本意為先后輸入10個(gè)數(shù),每輸入一個(gè)數(shù)后輸出它的平方值。由于在for()后加了一個(gè)分號(hào),使循環(huán)體變成了空語句,因此,只能輸入一個(gè)整數(shù)并輸出它的平方值。 又如:22 (10)輸入變量時(shí)忘記使用地址符。 例如: scanf("%d%d",a,b); 這是許多初學(xué)者剛學(xué)習(xí)C語言時(shí)一個(gè)常見的疏忽,或者說是習(xí)慣性的錯(cuò)誤,因?yàn)樵谄渌Z言中在輸入時(shí)只需要寫出變量名即可,而C語言要求指示:“向哪個(gè)地址標(biāo)識(shí)的單元送值”,應(yīng)寫成 scanf("%d%d",&a,&b); (10)輸入變量時(shí)忘記使用地址符。23 (11)括弧不配對(duì)。 當(dāng)一個(gè)語句中使用多層括弧時(shí)常出現(xiàn)括弧不配對(duì)的錯(cuò)誤,純屬粗心所致。例如:while((c=getchar()!='#')putchar(c); 少了一個(gè)右括弧。 (11)括弧不配對(duì)。24 (12)switch語句的各分支中漏寫break語句。 例如:switch(score){case5:printf("Verygood!");case4:printf("Good!");case3:printf("Pass!");case2:printf("Fail!");default:printf("dataerror!");} (12)switch語句的各分支中漏寫break語句25 上述switch語句的作用是希望根據(jù)score(成績(jī))打印出評(píng)語。但當(dāng)score的值為5時(shí),輸出為: VeryGood!Good!Pass!Fail!dataerror! 原因是漏寫了break語句。case只起標(biāo)號(hào)的作用,而不起判斷作用,因此在執(zhí)行完第一個(gè)printf函數(shù)語句后接著執(zhí)行第二、三、四、五個(gè)printf函數(shù)語句。應(yīng)改為 上述switch語句的作用是希望根據(jù)score(成績(jī))打26switch(score){case5:printf("Verygood!"); break;case4:printf("Good!"); break;case3:printf("Pass!"); break;case2:printf("Fail!"); break;default:printf("dataerror!");}switch(score)27 (13)引用數(shù)組元素時(shí)誤用了圓括弧。例如:main(){inti,a(10);for(i=0;i<10;i++)scanf("%d",&a(i));

} C語言中對(duì)數(shù)組的定義或引用數(shù)組元素時(shí)必須用方括弧。 (13)引用數(shù)組元素時(shí)誤用了圓括弧。28 14)對(duì)應(yīng)該有花括弧的復(fù)合語句,忘記加花括弧。 例如:sum=0;i=1;while(i<=100)sum=sum+i;i++; 本意是實(shí)現(xiàn)1+2+…+100,即。但上面的語句只是重復(fù)了sum+i的操作,而且循環(huán)永不終止。因?yàn)閕的值始終沒有改變。錯(cuò)誤在于沒有寫成復(fù)合語句形式。因此while語句的范圍到其后第一個(gè)分號(hào)為止。語句“i++;”不屬于循環(huán)體范圍之內(nèi)。應(yīng)改為 14)對(duì)應(yīng)該有花括弧的復(fù)合語句,忘記加花括弧。29while(i<=100){sum=sum+i;i++;}while(i<=100)30 (15)在用標(biāo)識(shí)符時(shí),忘記了大寫字母和小寫字母的區(qū)別。例如:main(){inta,b,c;a=2;b=3;C=A+B;printf("%d+%d=%",A,B,C);} 編譯時(shí)出錯(cuò)。編譯程序把a(bǔ)和A認(rèn)作是兩個(gè)不同的變量名處理,同樣,b和B,c和C都分別代表兩個(gè)不同的變量。 (15)在用標(biāo)識(shí)符時(shí),忘記了大寫字母和小寫字母的區(qū)別。31 (16)在定義數(shù)組時(shí),將定義的“元素個(gè)數(shù)”誤認(rèn)為是“可使用的最大下標(biāo)值”。例如:main(){staticinta[10]={1,2,3,4,5,6,7,8,9,10};inti;for(i=1;i<=10;i++)printf("%d",a[i]);} 想輸出a[1]到a[10],但在數(shù)組初始化時(shí)只給a[0]到a[9]賦了值,并未對(duì)a[10]賦值。這是一些初學(xué)者常犯的錯(cuò)誤。C語言規(guī)定:定義時(shí)用a[10],表示a數(shù)組有10個(gè)元素,而不是可以用的最大下標(biāo)值為10。 (16)在定義數(shù)組時(shí),將定義的“元素個(gè)數(shù)”誤認(rèn)為是“可32 (17)誤以為數(shù)組名代表數(shù)組中全部元素。例如:main(){staticinta[4]={1,3,5,7};printf("%d%d%d%d\n",a);} 企圖用數(shù)組名代表全部元素。在C語言中,數(shù)組名代表數(shù)組首地址,不能通過數(shù)組名輸出4個(gè)整數(shù)。 (17)誤以為數(shù)組名代表數(shù)組中全部元素。33 (18)對(duì)二維或多維數(shù)組的定義和引用的方法不對(duì)。例如:main(){inta[5,4];

printf("%d",a[1+2,2+2]);

} (18)對(duì)二維或多維數(shù)組的定義和引用的方法不對(duì)。34 對(duì)二維數(shù)組和多維數(shù)組在定義和引用時(shí)必須將每一維的數(shù)據(jù)分別用方括弧括起來。上面a[5,4]應(yīng)改為a[5][4],a[1+2,2+2]應(yīng)改為a[1+2][2+2]。根據(jù)C語言的語法規(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行的首地址。 對(duì)二維數(shù)組和多維數(shù)組在定義和引用時(shí)必須將每一維的數(shù)據(jù)分別35 (19)混淆字符數(shù)組與字符指針的區(qū)別。例如:main(){charstr[4];str="Computerandc";printf("%s\n",str);} (19)混淆字符數(shù)組與字符指針的區(qū)別。36 編譯出錯(cuò)。str是數(shù)組名,代表數(shù)組首地址。在編譯時(shí),對(duì)str數(shù)組分配了一段內(nèi)存單元,因此,在程序運(yùn)行期間str是一個(gè)常量,不能再被賦值。故str="Computerandc"是錯(cuò)誤的。如果把“charstr[4];”改成“char*str;”,則程序正確。此時(shí)str是指向字符數(shù)據(jù)的指針變量,str="Computerandc"是合法的,它將字符串的首地址賦給指針變量str,然后在printf函數(shù)語句中輸出字符串"Computerandc"。 因此應(yīng)當(dāng)弄清楚字符數(shù)組與字符指針變量的區(qū)別。 編譯出錯(cuò)。str是數(shù)組名,代表數(shù)組首地址。在編譯時(shí),對(duì)s37 (20)用指針變量之前沒有賦值。例如:main({char*p;scanf("%s",p);

} 沒有給指針變量p賦值就引用它,編譯時(shí)給出警告信息。應(yīng)當(dāng)改為char*p,c[20];p=c;scanf("%s",p); (20)用指針變量之前沒有賦值。38 即先根據(jù)需要定義一個(gè)大小合適的字符數(shù)組c,然后將c數(shù)組的首地址賦給指針變量p,此時(shí),p有確定的值指向數(shù)組c。再執(zhí)行scanf函數(shù)就沒有問題了,程序會(huì)把從鍵盤輸入的字符串存放到字符數(shù)組c中。 (21)不同類型的指針混用。例如:main(){inti=3,*p1;floata=1.5;*p2;p1=&i;p2=&a;p2=p1;printf("%d,%d\n",*p1,*p2);}

即先根據(jù)需要定義一個(gè)大小合適的字符數(shù)組c,然后將c數(shù)組的39 企圖使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指向整型變量。指向不同類型的指針間的賦值必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換。如: p2=(float*)p1; 作用是先將p1的值轉(zhuǎn)換成指向?qū)嵭偷闹羔?,然后再賦給p2。 這種情況在C程序中是常見的。例如,用malloc函數(shù)開辟內(nèi)存單元,函數(shù)返回的是指向字符的指針,而人們希望開辟的是存放一個(gè)結(jié)構(gòu)體變量值的存儲(chǔ)單元,要求得到指向該結(jié)構(gòu)體變量的指針,可以進(jìn)行類型轉(zhuǎn)換。如: 企圖使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指?0structstudent{intnum;charname[20];floatscore;};structstudentstudent1,*p;

p=(structstudent*)malloc(LEN); p是指向structstudent結(jié)構(gòu)體類型數(shù)據(jù)的指針,將malloc函數(shù)返回的字符指針轉(zhuǎn)換成指向structstudent類型的指針。structstudent41 (22)所調(diào)用函數(shù)在調(diào)用語句之后才定義,而又在調(diào)用前未加說明。例如:main(){floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);}floatmax(x,y)floatx,y;{return(z=x>y?x:y);}

(22)所調(diào)用函數(shù)在調(diào)用語句之后才定義,而又在調(diào)用前未42 這個(gè)程序乍看起來沒有什么問題,但在編譯時(shí)有出錯(cuò)信息。原因是,max函數(shù)是實(shí)型的,而且在main函數(shù)之后才定義,也就是max函數(shù)的定義位置在main函數(shù)中的調(diào)用max函數(shù)之后。改錯(cuò)的方法可以用下面兩種方法之一。 ①在main函數(shù)中增加一個(gè)對(duì)max函數(shù)的說明,即main(){floatmax(); /*說明將要用到的max函數(shù)為實(shí)型*/{floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);} 這個(gè)程序乍看起來沒有什么問題,但在編譯時(shí)有出錯(cuò)信息。原因43 ②將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即floatmax(x,y)floatx,y;{return(z=x>y?x:y);}main(){floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);} 這樣,編譯時(shí)不會(huì)出錯(cuò),程序運(yùn)行結(jié)果是正確的。 ②將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即44 (23)將函數(shù)的形參和函數(shù)中的局部變量一起定義。例如:max(x,y)intx,y,z;{z=x>y?x:y;return(z);} 形參應(yīng)該在函數(shù)體之前定義,而函數(shù)中用到的局部變量應(yīng)在函數(shù)體中定義。應(yīng)改為 (23)將函數(shù)的形參和函數(shù)中的局部變量一起定義。45max(x,y)intx,y;{intz;z=x>y?x:y;return(z);}max(x,y)46 (24)函數(shù)的實(shí)參和形參類型不一致。例如:main(){inta=3;b=4;c=fun(a,b);

}fun(x,y)floatx,y;{

} 實(shí)參a、b為整型,形參x、y為實(shí)型。a和b的值傳遞給x和y時(shí),x和y的值并非3和4。C要求實(shí)參與形參的類型一致。 (24)函數(shù)的實(shí)參和形參類型不一致。47 (25)沒有注意函數(shù)參數(shù)的求值順序。例如:i=3;printf("%d,%d\n",i,++i,++i); 許多人認(rèn)為輸出必然是 3,4,5 實(shí)際不盡然。在許多系統(tǒng)中輸出是 5,5,4 因?yàn)樵S多系統(tǒng)是采取自右至左的順序求函數(shù)參數(shù)值的。先求出最右面一個(gè)參數(shù)(++i)的值為4,再求出第2個(gè)參數(shù)(++i)的值為5,最后求出最左面的參數(shù)(i)的值為5。 (25)沒有注意函數(shù)參數(shù)的求值順序。48 C標(biāo)準(zhǔn)沒有具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而左。但每個(gè)C編譯程序都有自己的順序,在有些情況下,從左到右求解和從右到左求解的結(jié)果是相同的。例如: fun1(a+b,b+c,c+a); fun1是一個(gè)函數(shù)名,a+b、b+c、c+a是3個(gè)實(shí)參表達(dá)式。在一般情況下,自左至右地求這三個(gè)表達(dá)式的值和自右至左地求它們的值是一樣的,但在前面的例子中卻不相同。因此,建議最好不用會(huì)引起二義性的用法。如果在上例中,希望輸出“3,4,5”時(shí),可以改用 i=3;j=i+1;k=j+1; printf("%d,%d,%d\n",i,j,k); C標(biāo)準(zhǔn)沒有具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而49 (26)混淆數(shù)組名與指針變量的區(qū)別。例如:main(){inti,a[5];for(i=0;i<5;i++)scanf("%d",a++);

} 企圖通過a的改變使指針下移,每次指向欲輸入數(shù)據(jù)的數(shù)組元素。它的錯(cuò)誤在于:不了解數(shù)組名代表數(shù)組首地址,它的值是不能改變的,用a++是錯(cuò)誤的,應(yīng)當(dāng)用指針變量來指向各數(shù)組元素。即 (26)混淆數(shù)組名與指針變量的區(qū)別。50inti,a[5],*p;p=a;for(i=0;i<5;i++)scanf("%d",p++);或者inta[5],*p;for(p=a;p<a+5;p++)scanf("%d",p);inti,a[5],*p;51 (27)誤認(rèn)為形參值的改變會(huì)影響實(shí)參的值。例如:main(){inta,b;a=3;b=4;swap(a,b);printf("%d,%d\n",a,b);}swap(x,y)intx,y;{intt;t=x;x=y;y=t;} (27)誤認(rèn)為形參值的改變會(huì)影響實(shí)參的值。52 原意是通過調(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)該用指針變量。用指針變量作函數(shù)參數(shù),使指針變量所指向的變量的值發(fā)生變化。此時(shí)變量的值改變了,主調(diào)函數(shù)中可以利用這些已改變的值。如: 原意是通過調(diào)用swap函數(shù)使a和b的值對(duì)換,然后在mai53main(){inta,b,*P1,*P2;a=3;b=4;p1=&a;p2=&b;swap(p1,p2);printf("%d,%d\n",a,b);/*a和b的值已對(duì)換*/}swap(pt1,pt2)int*pt1,*pt2;{intt;t=*pt1;*pt1=*pt2;*pt2=t;}main()54 (28)混淆結(jié)構(gòu)體類型與結(jié)構(gòu)體變量的區(qū)別,對(duì)一個(gè)結(jié)構(gòu)體類型賦值。例如:structworker{longintnum;charname[20];charsex;intage;};worker.num=187045;strcpy(,"ZhangFan");worker.sex='M';worker.age=18; (28)混淆結(jié)構(gòu)體類型與結(jié)構(gòu)體變量的區(qū)別,對(duì)一個(gè)結(jié)構(gòu)體55 這是錯(cuò)誤的,只能對(duì)變量賦值而不能對(duì)類型賦值。上面只定義了structworker類型而未定義變量。應(yīng)改為structworker{longintnum;charname[20];charsex;intage;}; 這是錯(cuò)誤的,只能對(duì)變量賦值而不能對(duì)類型賦值。上面只定義了56structworkerworker_1;worker_1.num=187045;strcpy(worker_1.name,"ZhangFan");worker_1.sex='M';worker_1.age=18; 今定義了結(jié)構(gòu)體變量worker_1,并對(duì)其中的各成員賦值。structworkerworker_1;57 (29)使用文件時(shí)忘記打開,或打開方式與使用情況不匹配。 例如:if((fp=fopen("test","r"))==NULL){printf("cannotopenthisfile\n");exit(0);}ch=fgetc(fp);while(ch!='#'){ch=ch+4;fputc(ch,fp);ch=fget(fp);} (29)使用文件時(shí)忘記打開,或打開方式與使用情況不匹配58 對(duì)以“r”方式(只讀方式)打開的文件,進(jìn)行既讀又寫的操作,顯然是不行的。 此外,有的程序常忘記關(guān)閉文件,雖然系統(tǒng)會(huì)自動(dòng)關(guān)閉所用文件,但可能會(huì)丟失數(shù)據(jù)。因此必須在用完文件后關(guān)閉它。 以上只是列舉了一些初學(xué)者常出現(xiàn)的錯(cuò)誤,這些錯(cuò)誤大多是由于對(duì)C語言的語法不熟悉造成的。對(duì)C語言使用多了,熟練了,錯(cuò)誤自然就會(huì)減少了。在深入使用C語言后,還會(huì)出現(xiàn)其他一些更深入、更隱蔽的錯(cuò)誤。 對(duì)以“r”方式(只讀方式)打開的文件,進(jìn)行既讀又寫的操作59 程序出錯(cuò)有下面兩種情況。 ①語法錯(cuò)誤。指違背了C語言的語法的規(guī)定,對(duì)這類錯(cuò)誤,編譯程序一般能給出“出錯(cuò)信息”,并且告訴你在哪一行出錯(cuò)。只要細(xì)心,錯(cuò)誤是可以很快發(fā)現(xiàn)并排除的。 ②邏輯錯(cuò)誤。程序并沒有違背語法規(guī)則,但程序執(zhí)行結(jié)果與原意不符。這是由于程序設(shè)計(jì)人員通知給系統(tǒng)的指令與原意不相同,即出現(xiàn)了邏輯上的混亂。例如,前面第14條錯(cuò)誤:sum=0;i=1;while(i<=100)sum=sum+i;i++; 程序出錯(cuò)有下面兩種情況。60 語法并無錯(cuò)誤。但while語句通知給系統(tǒng)的信息是:當(dāng)i≤100時(shí),執(zhí)行“sum=sum+i;”。C語言系統(tǒng)無法辨別程序中這個(gè)語句是否符合作者的原意,而只能忠實(shí)地執(zhí)行這一指令。這種錯(cuò)誤比語法錯(cuò)誤更難檢查,要求程序員有較豐富的經(jīng)驗(yàn)。 語法并無錯(cuò)誤。但while語句通知給系統(tǒng)的信息是:61第13章程序調(diào)試與常見錯(cuò)誤分析13.1程序調(diào)試13.2常見錯(cuò)誤分析第13章程序調(diào)試與常見錯(cuò)誤分析13.1程序調(diào)試6213.1程序調(diào)試 13.1.1程序調(diào)試的步驟 所謂程序調(diào)試,是指對(duì)程序的查錯(cuò)和排錯(cuò)。調(diào)試程序一般應(yīng)經(jīng)過以下幾個(gè)步驟。 1.人工檢查,即靜態(tài)檢查 在寫好一個(gè)程序以后,不要匆匆忙忙上機(jī),而應(yīng)對(duì)紙面上的程序進(jìn)行人工檢查。這一步是十分重要的,它能發(fā)現(xiàn)程序設(shè)計(jì)人員由于疏忽而造成的多處錯(cuò)誤。而這一步驟往往容易被忽視。有的用戶總希望把一切推給計(jì)算機(jī)系統(tǒng)去做,但這樣就會(huì)多占用機(jī)器時(shí)間。13.1程序調(diào)試 13.1.1程序調(diào)試的步驟63 而且,作為一個(gè)程序設(shè)計(jì)人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)科學(xué)的作風(fēng),每一步都要嚴(yán)格把關(guān),不要把問題留給后面的工序。 為了更有效地進(jìn)行人工檢查,所編的程序應(yīng)力求做到以下幾點(diǎn): (1)應(yīng)當(dāng)采用結(jié)構(gòu)化程序方法編程,以增加可讀性。 (2)盡可能多地加注釋,以幫助理解每段程序的作用。 (3)在編寫復(fù)雜的程序時(shí),不要將全部語句都寫在main函數(shù)中,而要多利用函數(shù),用一個(gè)函數(shù)來實(shí)現(xiàn)一個(gè)單獨(dú)的功能。這樣既易于閱讀,也便于調(diào)試,各函數(shù)之間除用參數(shù)傳遞數(shù)據(jù)外,數(shù)據(jù)間應(yīng)盡量少出現(xiàn)耦合關(guān)系,以便于分別檢查和處理。 而且,作為一個(gè)程序設(shè)計(jì)人員應(yīng)當(dāng)養(yǎng)成嚴(yán)謹(jǐn)科學(xué)的作風(fēng),每一步64 2.上機(jī)調(diào)試,即動(dòng)態(tài)檢查 在人工(靜態(tài))檢查無誤后,才可以上機(jī)調(diào)試。通過上機(jī)發(fā)現(xiàn)的錯(cuò)誤稱之為動(dòng)態(tài)檢查。在編譯時(shí),系統(tǒng)會(huì)給出語法錯(cuò)誤的信息(包括哪一行有錯(cuò)以及錯(cuò)誤類型),用戶可以根據(jù)提示的信息具體找出程序中出錯(cuò)之處并進(jìn)行修改。應(yīng)當(dāng)注意的是:有時(shí)提示的出錯(cuò)行并不是真正出錯(cuò)的行,如果在提示出錯(cuò)的行上找不到錯(cuò)誤的話,應(yīng)當(dāng)?shù)缴弦恍性僬摇A硗?,有時(shí)提示出錯(cuò)的類型并非絕對(duì)準(zhǔn)確,由于出錯(cuò)的情況繁多而且各種錯(cuò)誤互有關(guān)聯(lián),因此要善于分析,找出真正的錯(cuò)誤,而不要死抱住提示的出錯(cuò)信息不放,鉆牛角尖。 2.上機(jī)調(diào)試,即動(dòng)態(tài)檢查65 如果系統(tǒng)提示的出錯(cuò)信息多,應(yīng)當(dāng)從上到下逐一改正。有時(shí)顯示出一大片錯(cuò)誤信息往往使人感到問題嚴(yán)重,無從下手。其實(shí)可能只有一兩個(gè)錯(cuò)誤。例如,對(duì)所用的變量未定義,編譯時(shí)就會(huì)對(duì)所有含該變量的語句發(fā)出出錯(cuò)信息。這時(shí)只要加上一個(gè)變量定義,那么所有錯(cuò)誤就都消除了。 3.運(yùn)行程序,試驗(yàn)數(shù)據(jù) 在改正語法錯(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)的。 如果系統(tǒng)提示的出錯(cuò)信息多,應(yīng)當(dāng)從上到下逐一改正。有時(shí)顯示66 有時(shí),數(shù)據(jù)比較復(fù)雜,難以立即判斷結(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ì)。例如,用 有時(shí),數(shù)據(jù)比較復(fù)雜,難以立即判斷結(jié)果是否正確??梢允孪?7 公式求根x的值,當(dāng)a0和b2-4ac>0時(shí),能得出正確結(jié)果;當(dāng)a=0或b2-4ac<0時(shí),就得不到正確結(jié)果(假設(shè)程序中未對(duì)a=0作防御處理以及未作復(fù)數(shù)處理)。因此,應(yīng)當(dāng)把程序可能遇到的多種方案都一一試到。再如,if語句有兩個(gè)分支,有可能在流程經(jīng)過其中一個(gè)分支的結(jié)果正確,而經(jīng)過另一個(gè)分支時(shí)結(jié)果不正確。這些都必須考慮周全。 13.1.2檢查和分析錯(cuò)誤原因 運(yùn)行結(jié)果不對(duì),大多屬于邏輯錯(cuò)誤。對(duì)這類錯(cuò)誤往往需要仔細(xì)檢查和分析才能發(fā)現(xiàn),可以采用的方法有以下幾種:

公式求根x的值,當(dāng)a0和b2-4ac>0時(shí),能得出正確68 (1)如果在程序中沒有發(fā)現(xiàn)問題,可以檢查流程圖有無錯(cuò)誤,即算法有無問題,如有,就需改正,接著修改程序。將程序與流程圖(或偽代碼)仔細(xì)對(duì)照,如果流程圖是正確的話,則是程序?qū)戝e(cuò)了,這很容易被發(fā)現(xiàn)的。例如,復(fù)合語句忘記寫花括弧“{}”,只要對(duì)照流程圖很快就能發(fā)現(xiàn)。 (2)如果實(shí)在找不到錯(cuò)誤,可以采取“分段檢查”的方法。在程序不同位置設(shè)幾個(gè)printf函數(shù)語句,輸出有關(guān)變量的值,逐段往下檢查,直到找到在某一段中數(shù)據(jù)不對(duì)的段為止。這時(shí)就已經(jīng)把錯(cuò)誤局限在這一段中了。不斷縮小“查錯(cuò)區(qū)”,就可能發(fā)現(xiàn)錯(cuò)誤所在。 (1)如果在程序中沒有發(fā)現(xiàn)問題,可以檢查流程圖有無錯(cuò)誤69 (3)也可以用第9章介紹過的“條件編譯”命令來進(jìn)行程序調(diào)試。在程序調(diào)試階段,若干printf函數(shù)語句要進(jìn)行編譯并執(zhí)行。當(dāng)調(diào)試完畢時(shí),這些語句不再編譯,也不再被執(zhí)行了。這種方法可以不必一一刪除printf函數(shù)語句,因此可以提高效率。 (4)有的系統(tǒng)還提供debug(調(diào)試)工具,跟蹤流程并給出相應(yīng)信息,使用更為方便,相關(guān)內(nèi)容請(qǐng)查閱有關(guān)手冊(cè)。 總之,程序調(diào)試是一項(xiàng)細(xì)致深入的工作,需要下功夫、動(dòng)腦子、善于累積經(jīng)驗(yàn)。在程序調(diào)試過程中往往反映出一個(gè)人的水平、經(jīng)驗(yàn)和科學(xué)態(tài)度。上機(jī)調(diào)試程序的目的決不是為了“驗(yàn)證程序的正確性”,而是“掌握調(diào)試方法和技術(shù)”。 (3)也可以用第9章介紹過的“條件編譯”命令來7013.2常見錯(cuò)誤分析 下面列舉出初學(xué)者易犯的錯(cuò)誤,以提醒讀者注意。 (1)誤把“=”作為“等于”比較符。 C語言中,“=”是賦值運(yùn)算符,“==”才是關(guān)系運(yùn)算符“等于”。如果寫成 if(a=b)printf("aequaltob");

13.2常見錯(cuò)誤分析 下面列舉出初學(xué)者易犯的錯(cuò)誤,以提71 C編譯程序會(huì)將(a=b)作為賦值表達(dá)式處理,將b的值賦給a,然后判斷a的值是否為0,若為非0,則作為“真”;若為0,則作為“假”。如果a的值為3,b的值為4,a≠b,按原意不應(yīng)輸出“aequaltob”。而現(xiàn)在先將b的值賦給a,a也為4,賦值表達(dá)式的值為4。if語句中的表達(dá)式值為“真”(非0),因此輸出“aequaltob”。 這種錯(cuò)誤在編譯時(shí)是檢查不出來的,但運(yùn)行結(jié)果往往是錯(cuò)的,而且,由于習(xí)慣的影響,程序設(shè)計(jì)者自己往往也不易發(fā)覺。 C編譯程序會(huì)將(a=b)作為賦值表達(dá)式處理,將b的值賦給72 (2)使用自加(++)和自減(--)運(yùn)算符時(shí)出錯(cuò)。例如:main(){int*p,a[5]={1,3,5,7,9};p=a;printf("%d",*p++);} 不少人認(rèn)為“*p++”的作用是先使p加1,即指向第1個(gè)元素a[1]處,然后輸出第一個(gè)元素a[1]的值3。其實(shí)應(yīng)該是,先執(zhí)行p++,而*p就是第0個(gè)元素a[0]的值1。結(jié)論是,先輸出a[0]的值,然后再使p加1。如果是*(++p),則先使p指向a[1],然后輸出a[1]的值。 (2)使用自加(++)和自減(--)運(yùn)算符時(shí)出錯(cuò)。73 (3)混淆字符和字符串的表示形式。例如:charsex;sex="M";

sex是字符變量,只能存放一個(gè)字符。而字符常量的形式是用單引號(hào)括起來的,應(yīng)改為 sex='M'; "M"是用雙引號(hào)括起來的字符串,它包括兩個(gè)字符M和\0,無法存放到字符變量sex中。 (3)混淆字符和字符串的表示形式。74 (4)語句后面漏分號(hào)。 C語言規(guī)定語句末尾必須有分號(hào)。分號(hào)是C語句中不可缺少的一部分。這也是和其他語言的不同之處。有的初學(xué)者往往忘記寫這一分號(hào)。如:a=3b=4 編譯時(shí),編譯程序在a=3后面未發(fā)現(xiàn)分號(hào),就把下一行b=4也作為上一行的語句的一部分,這樣就會(huì)出現(xiàn)語法錯(cuò)誤。有時(shí)編譯時(shí)指出某行有錯(cuò),但在該行上并未發(fā)現(xiàn)錯(cuò)誤,就應(yīng)該檢查上一行是否漏了分號(hào)。 (4)語句后面漏分號(hào)。75 (5)輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。 例如,若a已定義為整型,b已定義為實(shí)型。 a=3;b=4.5; printf("%f%d\n",a,b); 編譯時(shí)不給出出錯(cuò)信息,但運(yùn)行結(jié)果將與原意不符,輸出為 0.00000016402 它們并不是按照賦值的規(guī)則進(jìn)行轉(zhuǎn)換(如把4.5轉(zhuǎn)換成4),而是將數(shù)據(jù)在存儲(chǔ)單元中的形式按格式符的要求組織輸出(如b占4個(gè)字節(jié),只把最后兩個(gè)字節(jié)中的數(shù)據(jù)按%d作為整數(shù)輸出)。 (5)輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致。76 (6)忘記定義變量。例如:main(){x=3;y=6;printf("%d\n",x+y);} C語言要求對(duì)程序中用到的每一個(gè)變量都必須定義其類型,上面程序中沒有對(duì)x、y進(jìn)行定義。應(yīng)在函數(shù)體的開頭加上下面的語句。 intx,y; (6)忘記定義變量。77 (7)未注意int型數(shù)據(jù)的數(shù)值范圍。 一般微型計(jì)算機(jī)上使用的C語言編譯版本,為一個(gè)整型數(shù)據(jù)分配2個(gè)字節(jié)。因此,一個(gè)整數(shù)的范圍為-215~215-1,即-32768~32767。常見這樣的程序段:intnum;num=89101;printf("%d",num); 得到的卻是23565,原因是89101已超過整數(shù)所要求的范圍32767。2個(gè)字節(jié)容納不下89101,則將高位截去。 (7)未注意int型數(shù)據(jù)的數(shù)值范圍。78 (8)輸入時(shí)數(shù)據(jù)的組織與要求不符。 用scanf函數(shù)輸入數(shù)據(jù)時(shí),應(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)該用以下方法輸入。

(8)輸入時(shí)數(shù)據(jù)的組織與要求不符。79 34如果scanf函數(shù)為scanf("%d,%d",&a,&b); 對(duì)scanf函數(shù)中格式字符串中除了格式說明符外,對(duì)其他字符必須按原樣輸入。因此,應(yīng)按以下方法輸入。 3,4 此時(shí)如果用“34”反而錯(cuò)了。還應(yīng)注意,不能企圖用 scanf("inputa&b:%d,%d",&a,&b); 在屏幕上顯示一行信息: inputa&b: 3480 然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以為scanf具有BASIC語言中的INPUT語句的功能(先輸出一個(gè)字符串,再輸入變量的值)。如果想在屏幕上得到所需的提示信息,可以另加一個(gè)printf函數(shù)語句。printdf("inputa&b:");scanf("%d,%d",&a,&b); 然后在其后輸入a和b的值,這是不行的。這是由于有的讀者以81 (9)在不該加分號(hào)的地方加了分號(hào)。 例如:if(a>b);printf("aislargerthanb\n"); 本意為,當(dāng)a>b時(shí)輸出“aislargerthanb”的信息。但由于在“if(a>b)”后加了分號(hào),因此if語句到分號(hào)結(jié)束。即當(dāng)(a>b)為真時(shí),執(zhí)行一個(gè)空語句。本來想a≤b時(shí)不輸出上述信息,但現(xiàn)在printf函數(shù)語句并不從屬于if語句,而是與if語句平行的語句。不論a>b還是 a≤b,“aislargerthanb”都被輸出出來。 (9)在不該加分號(hào)的地方加了分號(hào)。82 又如:for(i=0;i<10;i++);{scanf("%d",&x);printf("%d\n",x*x);} 本意為先后輸入10個(gè)數(shù),每輸入一個(gè)數(shù)后輸出它的平方值。由于在for()后加了一個(gè)分號(hào),使循環(huán)體變成了空語句,因此,只能輸入一個(gè)整數(shù)并輸出它的平方值。 又如:83 (10)輸入變量時(shí)忘記使用地址符。 例如: scanf("%d%d",a,b); 這是許多初學(xué)者剛學(xué)習(xí)C語言時(shí)一個(gè)常見的疏忽,或者說是習(xí)慣性的錯(cuò)誤,因?yàn)樵谄渌Z言中在輸入時(shí)只需要寫出變量名即可,而C語言要求指示:“向哪個(gè)地址標(biāo)識(shí)的單元送值”,應(yīng)寫成 scanf("%d%d",&a,&b); (10)輸入變量時(shí)忘記使用地址符。84 (11)括弧不配對(duì)。 當(dāng)一個(gè)語句中使用多層括弧時(shí)常出現(xiàn)括弧不配對(duì)的錯(cuò)誤,純屬粗心所致。例如:while((c=getchar()!='#')putchar(c); 少了一個(gè)右括弧。 (11)括弧不配對(duì)。85 (12)switch語句的各分支中漏寫break語句。 例如:switch(score){case5:printf("Verygood!");case4:printf("Good!");case3:printf("Pass!");case2:printf("Fail!");default:printf("dataerror!");} (12)switch語句的各分支中漏寫break語句86 上述switch語句的作用是希望根據(jù)score(成績(jī))打印出評(píng)語。但當(dāng)score的值為5時(shí),輸出為: VeryGood!Good!Pass!Fail!dataerror! 原因是漏寫了break語句。case只起標(biāo)號(hào)的作用,而不起判斷作用,因此在執(zhí)行完第一個(gè)printf函數(shù)語句后接著執(zhí)行第二、三、四、五個(gè)printf函數(shù)語句。應(yīng)改為 上述switch語句的作用是希望根據(jù)score(成績(jī))打87switch(score){case5:printf("Verygood!"); break;case4:printf("Good!"); break;case3:printf("Pass!"); break;case2:printf("Fail!"); break;default:printf("dataerror!");}switch(score)88 (13)引用數(shù)組元素時(shí)誤用了圓括弧。例如:main(){inti,a(10);for(i=0;i<10;i++)scanf("%d",&a(i));

} C語言中對(duì)數(shù)組的定義或引用數(shù)組元素時(shí)必須用方括弧。 (13)引用數(shù)組元素時(shí)誤用了圓括弧。89 14)對(duì)應(yīng)該有花括弧的復(fù)合語句,忘記加花括弧。 例如:sum=0;i=1;while(i<=100)sum=sum+i;i++; 本意是實(shí)現(xiàn)1+2+…+100,即。但上面的語句只是重復(fù)了sum+i的操作,而且循環(huán)永不終止。因?yàn)閕的值始終沒有改變。錯(cuò)誤在于沒有寫成復(fù)合語句形式。因此while語句的范圍到其后第一個(gè)分號(hào)為止。語句“i++;”不屬于循環(huán)體范圍之內(nèi)。應(yīng)改為 14)對(duì)應(yīng)該有花括弧的復(fù)合語句,忘記加花括弧。90while(i<=100){sum=sum+i;i++;}while(i<=100)91 (15)在用標(biāo)識(shí)符時(shí),忘記了大寫字母和小寫字母的區(qū)別。例如:main(){inta,b,c;a=2;b=3;C=A+B;printf("%d+%d=%",A,B,C);} 編譯時(shí)出錯(cuò)。編譯程序把a(bǔ)和A認(rèn)作是兩個(gè)不同的變量名處理,同樣,b和B,c和C都分別代表兩個(gè)不同的變量。 (15)在用標(biāo)識(shí)符時(shí),忘記了大寫字母和小寫字母的區(qū)別。92 (16)在定義數(shù)組時(shí),將定義的“元素個(gè)數(shù)”誤認(rèn)為是“可使用的最大下標(biāo)值”。例如:main(){staticinta[10]={1,2,3,4,5,6,7,8,9,10};inti;for(i=1;i<=10;i++)printf("%d",a[i]);} 想輸出a[1]到a[10],但在數(shù)組初始化時(shí)只給a[0]到a[9]賦了值,并未對(duì)a[10]賦值。這是一些初學(xué)者常犯的錯(cuò)誤。C語言規(guī)定:定義時(shí)用a[10],表示a數(shù)組有10個(gè)元素,而不是可以用的最大下標(biāo)值為10。 (16)在定義數(shù)組時(shí),將定義的“元素個(gè)數(shù)”誤認(rèn)為是“可93 (17)誤以為數(shù)組名代表數(shù)組中全部元素。例如:main(){staticinta[4]={1,3,5,7};printf("%d%d%d%d\n",a);} 企圖用數(shù)組名代表全部元素。在C語言中,數(shù)組名代表數(shù)組首地址,不能通過數(shù)組名輸出4個(gè)整數(shù)。 (17)誤以為數(shù)組名代表數(shù)組中全部元素。94 (18)對(duì)二維或多維數(shù)組的定義和引用的方法不對(duì)。例如:main(){inta[5,4];

printf("%d",a[1+2,2+2]);

} (18)對(duì)二維或多維數(shù)組的定義和引用的方法不對(duì)。95 對(duì)二維數(shù)組和多維數(shù)組在定義和引用時(shí)必須將每一維的數(shù)據(jù)分別用方括弧括起來。上面a[5,4]應(yīng)改為a[5][4],a[1+2,2+2]應(yīng)改為a[1+2][2+2]。根據(jù)C語言的語法規(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行的首地址。 對(duì)二維數(shù)組和多維數(shù)組在定義和引用時(shí)必須將每一維的數(shù)據(jù)分別96 (19)混淆字符數(shù)組與字符指針的區(qū)別。例如:main(){charstr[4];str="Computerandc";printf("%s\n",str);} (19)混淆字符數(shù)組與字符指針的區(qū)別。97 編譯出錯(cuò)。str是數(shù)組名,代表數(shù)組首地址。在編譯時(shí),對(duì)str數(shù)組分配了一段內(nèi)存單元,因此,在程序運(yùn)行期間str是一個(gè)常量,不能再被賦值。故str="Computerandc"是錯(cuò)誤的。如果把“charstr[4];”改成“char*str;”,則程序正確。此時(shí)str是指向字符數(shù)據(jù)的指針變量,str="Computerandc"是合法的,它將字符串的首地址賦給指針變量str,然后在printf函數(shù)語句中輸出字符串"Computerandc"。 因此應(yīng)當(dāng)弄清楚字符數(shù)組與字符指針變量的區(qū)別。 編譯出錯(cuò)。str是數(shù)組名,代表數(shù)組首地址。在編譯時(shí),對(duì)s98 (20)用指針變量之前沒有賦值。例如:main({char*p;scanf("%s",p);

} 沒有給指針變量p賦值就引用它,編譯時(shí)給出警告信息。應(yīng)當(dāng)改為char*p,c[20];p=c;scanf("%s",p); (20)用指針變量之前沒有賦值。99 即先根據(jù)需要定義一個(gè)大小合適的字符數(shù)組c,然后將c數(shù)組的首地址賦給指針變量p,此時(shí),p有確定的值指向數(shù)組c。再執(zhí)行scanf函數(shù)就沒有問題了,程序會(huì)把從鍵盤輸入的字符串存放到字符數(shù)組c中。 (21)不同類型的指針混用。例如:main(){inti=3,*p1;floata=1.5;*p2;p1=&i;p2=&a;p2=p1;printf("%d,%d\n",*p1,*p2);}

即先根據(jù)需要定義一個(gè)大小合適的字符數(shù)組c,然后將c數(shù)組的100 企圖使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指向整型變量。指向不同類型的指針間的賦值必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換。如: p2=(float*)p1; 作用是先將p1的值轉(zhuǎn)換成指向?qū)嵭偷闹羔?,然后再賦給p2。 這種情況在C程序中是常見的。例如,用malloc函數(shù)開辟內(nèi)存單元,函數(shù)返回的是指向字符的指針,而人們希望開辟的是存放一個(gè)結(jié)構(gòu)體變量值的存儲(chǔ)單元,要求得到指向該結(jié)構(gòu)體變量的指針,可以進(jìn)行類型轉(zhuǎn)換。如: 企圖使p2也指向i,但p2是指向?qū)嵭妥兞康闹羔?,不能指?01structstudent{intnum;charname[20];floatscore;};structstudentstudent1,*p;

p=(structstudent*)malloc(LEN); p是指向structstudent結(jié)構(gòu)體類型數(shù)據(jù)的指針,將malloc函數(shù)返回的字符指針轉(zhuǎn)換成指向structstudent類型的指針。structstudent102 (22)所調(diào)用函數(shù)在調(diào)用語句之后才定義,而又在調(diào)用前未加說明。例如:main(){floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);}floatmax(x,y)floatx,y;{return(z=x>y?x:y);}

(22)所調(diào)用函數(shù)在調(diào)用語句之后才定義,而又在調(diào)用前未103 這個(gè)程序乍看起來沒有什么問題,但在編譯時(shí)有出錯(cuò)信息。原因是,max函數(shù)是實(shí)型的,而且在main函數(shù)之后才定義,也就是max函數(shù)的定義位置在main函數(shù)中的調(diào)用max函數(shù)之后。改錯(cuò)的方法可以用下面兩種方法之一。 ①在main函數(shù)中增加一個(gè)對(duì)max函數(shù)的說明,即main(){floatmax(); /*說明將要用到的max函數(shù)為實(shí)型*/{floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);} 這個(gè)程序乍看起來沒有什么問題,但在編譯時(shí)有出錯(cuò)信息。原因104 ②將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即floatmax(x,y)floatx,y;{return(z=x>y?x:y);}main(){floatx,y,z;x=3.5;y=-7.6;z=max(x,y);printf("%f\n",z);} 這樣,編譯時(shí)不會(huì)出錯(cuò),程序運(yùn)行結(jié)果是正確的。 ②將max函數(shù)的定義位置調(diào)到main函數(shù)之前。即105 (23)將函數(shù)的形參和函數(shù)中的局部變量一起定義。例如:max(x,y)intx,y,z;{z=x>y?x:y;return(z);} 形參應(yīng)該在函數(shù)體之前定義,而函數(shù)中用到的局部變量應(yīng)在函數(shù)體中定義。應(yīng)改為 (23)將函數(shù)的形參和函數(shù)中的局部變量一起定義。106max(x,y)intx,y;{intz;z=x>y?x:y;return(z);}max(x,y)107 (24)函數(shù)的實(shí)參和形參類型不一致。例如:main(){inta=3;b=4;c=fun(a,b);

}fun(x,y)floatx,y;{

} 實(shí)參a、b為整型,形參x、y為實(shí)型。a和b的值傳遞給x和y時(shí),x和y的值并非3和4。C要求實(shí)參與形參的類型一致。 (24)函數(shù)的實(shí)參和形參類型不一致。108 (25)沒有注意函數(shù)參數(shù)的求值順序。例如:i=3;printf("%d,%d\n",i,++i,++i); 許多人認(rèn)為輸出必然是 3,4,5 實(shí)際不盡然。在許多系統(tǒng)中輸出是 5,5,4 因?yàn)樵S多系統(tǒng)是采取自右至左的順序求函數(shù)參數(shù)值的。先求出最右面一個(gè)參數(shù)(++i)的值為4,再求出第2個(gè)參數(shù)(++i)的值為5,最后求出最左面的參數(shù)(i)的值為5。 (25)沒有注意函數(shù)參數(shù)的求值順序。109 C標(biāo)準(zhǔn)沒有具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而左。但每個(gè)C編譯程序都有自己的順序,在有些情況下,從左到右求解和從右到左求解的結(jié)果是相同的。例如: fun1(a+b,b+c,c+a); fun1是一個(gè)函數(shù)名,a+b、b+c、c+a是3個(gè)實(shí)參表達(dá)式。在一般情況下,自左至右地求這三個(gè)表達(dá)式的值和自右至左地求它們的值是一樣的,但在前面的例子中卻不相同。因此,建議最好不用會(huì)引起二義性的用法。如果在上例中,希望

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論