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

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論