C 語言PPT譚浩強教材配套版第16章.ppt_第1頁
C 語言PPT譚浩強教材配套版第16章.ppt_第2頁
C 語言PPT譚浩強教材配套版第16章.ppt_第3頁
C 語言PPT譚浩強教材配套版第16章.ppt_第4頁
C 語言PPT譚浩強教材配套版第16章.ppt_第5頁
已閱讀5頁,還剩55頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第16章常見錯誤和程序調試 16 1常見錯誤分析16 2程序調試 要真正學好C 用好C并不容易 靈活 固然是好事 但也使人難以掌握 尤其是初學者往往出了錯還不知怎么回事 C編譯程序對語法的檢查不如其他高級語言那樣嚴格 這是為了給程序人員留下 靈活 的余地 因此 往往要由程序設計者自己設法保證程序的正確性 調試一個C程序要比調試一個PASCAL或FORTRAN程序更困難一些 需要不斷積累經(jīng)驗 提高程序設計和調試程序的水平 C語言有些語法規(guī)定和其他高級語言不同 學習過其他高級語言的讀者往往按照使用其他高級語言的習慣來寫C程序 這也是出錯的一個原因 16 1常見錯誤分析下面將初學者在學習和使用C語言 不包括C 時容易犯的錯誤列舉出來 以起提醒的作用 這些內容在以前各章中大多已談到 為便于查閱 在本章中集中列舉 供初學者參考 以此為鑒 1 忘記定義變量 如 main x 3 y 6 printf d n x y C要求對程序中用到的每一個變量都必須定義其類型 上面程序中沒有對x y進行定義 應在函數(shù)體的開頭加intx y 這是學過BASIC和FORTRAN語言的讀者寫C程序時常見的一個錯誤 在BASIC語言中 可以不必先定義變量類型就可直接使用 在FORTRAN中 未經(jīng)定義類型的變量按隱含的I N規(guī)則決定其類型 而C語言則要求對用到的每一個變量都要在本函數(shù)中定義 除非已定義為外部變量 2 輸入輸出的數(shù)據(jù)的類型與所用格式說明符不一致 例如 若a已定義為整型 b已定義為實型 a 3 b 4 5 printf f d n a b 編譯時不給出出錯信息 但運行結果將與原意不符 輸出為0 00000016402它們并不是按照賦值的規(guī)則進行轉換 如把4 5轉換成4 而是將數(shù)據(jù)在存儲單元中的形式按格式符的要求組織輸出 如b占4個字節(jié) 只把最后兩個字節(jié)中的數(shù)據(jù)按 d 作為整數(shù)輸出 3 未注意int型數(shù)據(jù)的數(shù)值范圍 一般微型計算機上使用的C編譯系統(tǒng) 對一個整型數(shù)據(jù)分配兩個字節(jié) 因此一個整數(shù)的范圍為 215 215 1 即 32768 32767 常見這樣的程序段 intnum num 89101 printf d num 得到的卻是23565 原因是89101已超過32767 兩個字節(jié)容納不下89101 則將高位截去 見圖16 1 即將超過低16位的數(shù)截去 即將89101減去216 即16位二進制所形成的模 89101 65536 23565 圖16 1有時還會出現(xiàn)負數(shù) 例如num 196607 輸出得 1 因為196607的二進制形式為00000000000000101111111111111111去掉高位10 低16位的值是 1 1的補碼是 1111111111111111 對于超過整個范圍的數(shù) 要用long型 即改為longintnum num 89101 printf ld num 請注意 如果只定義num為long型 而在輸出時仍用 d 說明符 仍會出現(xiàn)以上錯誤 4 輸入變量時忘記使用地址符 如 scanf d d a b 這是許多初學者剛學習C語言時一個常見的疏忽 或者說是習慣性的錯誤 因為在其他語言中在輸入時只需寫出變量名即可 而C語言要求指明 向哪個地址標識的單元送值 應寫成scanf d d 5 輸入時數(shù)據(jù)的組織與要求不符 用scanf函數(shù)輸入數(shù)據(jù) 應注意如何組織輸入 數(shù)據(jù) 假如有以下scanf函數(shù) scanf d d 對scanf函數(shù)中格式字符串中除了格式說明符外 對其他字符必須按原樣輸入 因此 應按以下方法輸入 3 4 此時如果用 34 反而錯了 還應注意 不能企圖用scanf inputa scanf d d 若為零作為假 如果a的值為3 b的值為4 a b 按原意不應輸出 aequaltob 而現(xiàn)在先將b的值賦給a a也為4 賦值表達式的值為4 if語句中的表達式值為真 非零 因此輸出 aequaltob 這種錯誤在編譯時是檢查不出來的 但運行結果往往是錯的 而且由于習慣的影響 程序設計者自己往往也不易發(fā)覺 7 語句后面漏分號 C語言規(guī)定語句末尾必須有分號 分號是C語句不可缺少的一部分 這也是和其他語言不同的 有的初學者往往忘記寫這一分號 如 a 3b 4編譯時 編譯程序在 a 3 后面未發(fā)現(xiàn)分號 就把下一行 b 4 也作為上一行的語句的一部分 這就出現(xiàn)語法錯誤 有時編譯時指出某行有錯 但在該行上并未發(fā)現(xiàn)錯誤 應該檢查上一行是否漏了分號 如果用復合語句 有的學過PASCAL語言的讀者往往漏寫最后一個語句的分號 如 t a a b b t 在PASCAL中分號是兩個語句間的分隔符而不是語句的一部分 而在C中 沒有分號的就不是語句 8 在不該加分號的地方加了分號 例如 if a b printf aislargerthanb n 本意為當a b時輸出 aislargerthanb 的信息 但由于在if a b 后加了分號 因此if語句到此結束 即當 a b 為真時 執(zhí)行一個空語句 本來想a b時不輸出上述信息 但現(xiàn)在printf函數(shù)語句并不從屬于if語句 而是與if語句平行的語句 不論 a b還是a b 都輸出 aislargerthanb 又如 for i 0 i 10 i scanf d 本意為先后輸入10個數(shù) 每輸入一個數(shù)后輸出它的平方值 由于在for 后加了一個分號 使循環(huán)體變成了空語句 只能輸入一個整數(shù)并輸出它的平方值 總之 在if for while語句中 不要畫蛇添足多加分號 9 對應該有花括弧的復合語句 忘記加花括弧 如 sum 0 i 1 while i 100 sum sum i i 本意是實現(xiàn)1 2 100 即 i 但上面的語句只是重復了sum 1的操作 而且循環(huán)永不終止 因為i的值始終沒有改變 錯誤在于沒有寫成復合語句形式 因此while語句的范圍到其后第一個分號為止 語句 i 不屬于循環(huán)體范圍之內 應改 100i 0 為while i 100 sum sum i i 10 括弧不配對 當一個語句中使用多層括弧時常出現(xiàn)這類錯誤 純屬粗心所致 如 while c getchar putchar c 少了一個右括弧 11 在用標識符時 忘記了大寫字母和小寫字母的區(qū)別 例如 main inta b c a 2 b 3 C A B printf d d A B C 編譯時出錯 編譯程序把a和A認作是兩個不同的變量名處理 同樣b和B c和C都分別代表兩個不同的變量 12 引用數(shù)組元素時誤用了圓括弧 如 main inti a 10 for i 0 i 10 i scanf d C語言中對數(shù)組的定義或引用數(shù)組元素時必須用方括弧 13 在定義數(shù)組時 將定義的 元素個數(shù) 誤認為是 可使用的最大下標值 main inta 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 但一些初學者常犯的錯誤 C語言規(guī)定定義時用a 10 表示a數(shù)組有10個元素 而不是可以用的最大下標值為10 數(shù)組只包括a 0 到a 9 10個元素 因此用a 10 就超出a數(shù)組的范圍了 14 對二維或多維數(shù)組的定義和引用的方法不對 main inta 5 4 printf d a 1 2 2 2 對二維數(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ù)輸出的結果并不是a 3 4 的值 而是a數(shù)組第4行的首地址 15 誤以為數(shù)組名代表數(shù)組中全部元素 例如 main inta 4 1 3 5 7 printf d d d d n a 企圖用數(shù)組名代表全部元素 在C語言中 數(shù)組名代表數(shù)組首地址 不能通過數(shù)組名輸出4個整數(shù) 16 混淆字符數(shù)組與字符指針的區(qū)別 main charstr 4 str Computerandc printf s n str 編譯出錯 str是數(shù)組名 代表數(shù)組首地址 在編譯時對str數(shù)組分配了一段內存單元 因此在程序運行期間str是一個常量 不能再被賦值 因此 str Computerandc 是錯誤的 如果把 charstr 4 改成 char str 則程序正確 此時str是指向字符數(shù)據(jù)的指針變量 str Computerandc 是合法的 它將字符串的首地址賦給指針變量str 然后在printf函數(shù)語句中輸出字符串 Computerandc 因此應當弄清楚字符數(shù)組與字符指針變量用法的區(qū)別 17 在引用指針變量之前沒有對它賦予確定的值 main char p scanf s p 沒有給指針變量p賦值就引用它 編譯時給出警告信息 應當改為char p c 20 p c scanf s p 即先根據(jù)需要定義一個大小合適的字符數(shù)組c 然后將c數(shù)組的首地址賦給指針變量p 此時p有確定的值 指向數(shù)組c 再執(zhí)行scanf函數(shù)就沒有問題了 把從鍵盤輸入的字符串存放到字符數(shù)組c中 18 switch語句的各分支中漏寫break語句 例如 switch score case5 printf erygood case4 printf Good case3 printf Pass case2 printf Fail defult printf dataerror 上述switch語句的作用是希望根據(jù)score 成績 打印出評語 但當score的值為5時 輸出為 eryGood Good Pass Fail dataerror 原因是漏寫了break語句 case只起標號的作用 而不起判斷作用 因此在執(zhí)行完第一個printf函數(shù)語句后接著執(zhí)行第2 3 4 5個printf函數(shù)語句 應改為switch score case5 printf erygood break case4 printf Good break case3 printf Pass break case2 print Fail break defult print dataerror 19 混淆字符和字符串的表示形式 charsex sex M sex是字符變量 只能存放一個字符 而字符常量的形式是用單引號括起來的 應改為sex M M 是用雙引號括起來的字符串 它包括兩個字符 M 和 0 無法存放到字符變量sex中 20 使用自加 和自減 運算符時出的錯誤 例如 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 的作用是先用p的原值 用完后使p加1 p的原值指向數(shù)組a的第0個元素a 0 因此 p就是第0個元素a 0 的值1 結論是先輸出a 0 的值 然后再使p加1 如果是 p 則先使p指向a 1 然后輸出a 1 的值 21 有人習慣用傳統(tǒng)的方式對函數(shù)形參進行聲明 但卻把對函數(shù)的形參和函數(shù)中的局部變量混在一起定義 如 max x y intx y x y x y return 應改為max x y intx y int x y x y return 22 所調用的函數(shù)在調用語句之后才定義 而又在調用前未加說明 main floatx y x 3 5 y 7 6 max x y printf f n floatmax floatx floaty return x y x y 這個程序乍看起來沒有什么問題 但在編譯時有出錯信息 原因是max函數(shù)是實型的 而且在main函數(shù)之后才定義 也就是max函數(shù)的定義位置在main函數(shù)中的調用max函數(shù)之后 改錯的方法可以用以下二者之一 在main函數(shù)中增加一個對max函數(shù)的聲明 即函數(shù)的原型 main floatmax float float 聲明將要用到的max函數(shù)為實型 floatx y x 3 5 y 7 6 max x y printf f n 將max函數(shù)的定義位置調到main函數(shù)之前 即 floatmax floatx floaty return x y x y main floatx y x 3 5 y 7 6 max x y printf f n 這樣 編譯時不會出錯 程序運行結果是正確的 23 誤認為形參值的改變會影響實參的值 main inta b a 3 b 4 swap a b printf d d n a b swap intx inty intt t x x y y t 原意是通過調用swap函數(shù)使a和b的值對換 然后在main函數(shù)中輸出已對換了值的a和b 但是這樣的程序是達不到目的的 因為x和y的值的變化是不傳送回實參a和b的 main函數(shù)中的a和b的值并未改變 如果想從函數(shù)得到一個以上的變化了的值 應該用指針變量 用指針變量作函數(shù)參數(shù) 使指針變量所指向的變量的值發(fā)生變化 此時變量的值改變了 主調函數(shù)中可以利用這些已改變的值 如 main inta b p1 p2 a 3 b 4 p1 24 函數(shù)的實參和形參類型不一致 main inta 3 b 4 c fun a b fun floatx floaty 實參a b為整型 形參x y為實型 a和b的值傳遞給x和y時 x和y的值并非3和4 C要求實參與形參的類型一致 如果在main函數(shù)中對fun作原型 聲明 fun float float 程序可以正常運行 此時 按不同類型間的賦值的規(guī)則處理 在虛實結合后x 3 0 y 4 0 也可以將fun函數(shù)的位置調到main函數(shù)之前 也可獲正確結果 25 不同類型的指針混用 main inti 3 p1 floata 1 5 p2 p1 p2 p1 printf d d n p1 p2 企圖使p2也指向i 但p2是指向實型變量的指針 不能指向整型變量 指向不同類型的指針間的賦值必須進行強制類型轉換 如 p2 float p1 作用是先將p1的值轉換成指向實型的指針 然后再賦給p2 這種情況在C程序中是常見的 例如 用malloc函數(shù)開辟內存單元 函數(shù)返回的是指向被分配內存空間的void 類型的指針 而人們希望開辟的是 存放一個結構體變量值的存儲單元 要求得到指向該結構體變量的指針 可以進行如下的類型轉換 structstudent intnum charname 20 floatscore structstudentstudent1 p p structstudent malloc LEN p是指向structstudent結構體類型數(shù)據(jù)的指針 將malloc函數(shù)返回的void 類型指針轉換成指向structstudent類型變量的指針 26 沒有注意函數(shù)參數(shù)的求值順序 例如有以下語句 i 3 printf d d d n i i i 許多人認為輸出必然是3 4 5實際不盡然 在TurboC和其他一些C系統(tǒng)中輸出是5 5 4 因為這些系統(tǒng)是采取自右至左的順序求函數(shù)參數(shù)的值 先求出最右面一個參數(shù) i 的值為4 再求出第2個參數(shù) i 的值為5 最后求出最左面的參數(shù) i 的值5 C標準沒有具體規(guī)定函數(shù)參數(shù)求值的順序是自左而右還是自右而左 但每個C編譯程序都有自己的順序 在有些情況下 從左到右求解和從右到左求解的結果是相同的 例如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 d n i j k 27 混淆數(shù)組名與指針變量的區(qū)別 main inti a 5 for i 0 i 5 i scanf d a 企圖通過a的改變使指針下移 每次指向欲輸入數(shù)據(jù)的數(shù)組元素 它的錯誤在于不了解數(shù)組名代表數(shù)組首地址 它的值是不能改變的 用a 是錯誤的 應當用指針變量來指向各數(shù)組元素 即 inti 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 28 混淆結構體類型與結構體變量的區(qū)別 對一個結構體類型賦值 structworker longintnum charname 20 charsex intage worker num 187045 strcpy worker name ZhangFun worker sex M worker age 18 這是錯誤的 只能對變量賦值而不能對類型賦值 上面只定義了structworker類型而未定義變量 應改為structworker longintnum charname 20 charsex intage structworkerworker 1 worker 1 num 187045 strcpy worker 1 name ZhangFun worker 1 sex M worker 1 age 18 今定義了結構體變量worker 1 并對其中的各成員賦值 29 使用文件時忘記打開 或打開方式與使用情況不匹配 例如 對文件的讀寫 用只讀方式打開 卻企圖向 該文件輸出數(shù)據(jù) 例如 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 對以 r 方式 只讀方式 打開的文件 進行既讀又寫的操作 顯然是不行的 此外 有的程序常忘記關閉文件 雖然系統(tǒng)會自動關閉所用文件 但可能會丟失數(shù)據(jù) 因此必須在用完文件后關閉它 以上只是列舉了一些初學者常出現(xiàn)的錯誤 這些錯誤大多是對于C語法不熟悉之故 對C語言使用多了 比較熟練了 犯這些錯誤自然就會減少了 在深入使用C語言后 還會出現(xiàn)其他一些更深入 更隱蔽的錯誤 程序出錯有三種情況 語法錯誤 指違背了C語法的規(guī)定 對這類錯誤 編譯程序一般能給出 出錯信息 并且告訴你在哪一行出錯 只要細心 是可以很快發(fā)現(xiàn)并排除的 邏輯錯誤 程序并無違背語法規(guī)則 但程序執(zhí)行結果與原意不符 這是由于程序設計人員設計的算法有錯或編寫程序有錯 通知給系統(tǒng)的指令與解題的原意不相同 即出現(xiàn)了邏輯上的混亂 例如 前面第9條錯誤 sum 0 i 1 while i 100 sum sum i i 語法并無錯誤 但while語句通知給系統(tǒng)的信息是當i 100時 執(zhí)行 sum sum i C系統(tǒng)無法辨別程序中這個語句是否符合作者的原意 而只能忠實地執(zhí)行這一指令 這種錯誤比語法錯誤更難檢查 要求程序員有較豐富的經(jīng)驗 運行錯誤 程序既無語法錯誤 也無邏輯錯誤 但在運行時出現(xiàn)錯誤甚至停止運行 例如 inta b c scanf d d 輸入a和b的值 輸出b a的值 程序沒有錯 但是如果輸入a的值為0 就會出現(xiàn)錯誤 因此程序應能適應不同的數(shù)據(jù) 或者說能經(jīng)受各種數(shù)據(jù)的 考驗 具有 健壯性 寫完一個程序只能說完成任務的一半 甚至不到一半 調試程序往往比寫程序更難 更需要精力 時間和經(jīng)驗 常常有這樣的情況 程序花一天就寫完了 而調試程序二三天也未能完 有時一個小小的程序會出錯五六處 而發(fā)現(xiàn)和排除一個錯誤 有時竟需要半天 甚至更多 希望讀者通過實踐掌握調試程序的方法和技術 16 2程序調試所謂程序調試是指對程序的查錯和排錯 調試程序一般應經(jīng)過以下幾個步驟 1 先進行人工檢查 即靜態(tài)檢查 在寫好一個程序以后 不要匆匆忙忙上機 而應對紙面上的程序進行人工檢查 這一步是十分重要的 它能發(fā)現(xiàn)程序設計人員由于疏忽而造成的多數(shù)錯誤 而這一步驟往往容易被人忽視 有人總希望把一切推給計算機系統(tǒng)去做 但這樣就會多占用機器時間 而且 作為一個程序人員應當養(yǎng)成嚴謹?shù)目茖W作風 每一步都要嚴格把關 不把問題留給后面的工序 為了更有效地進行人工檢查 所編的程序應注意力 求做到以下幾點 應當采用結構化程序方法編程 以增加可讀性 盡可能多加注釋 以幫助理解每段程序的作用 在編寫復雜的程序時 不要將全部語句都寫在main函數(shù)中 而要多利用函數(shù) 用一個函數(shù)來實現(xiàn)一個單獨的功能 這樣既易于閱讀也便于調試 各函數(shù)之間除用參數(shù)傳遞數(shù)據(jù)這一渠道以外 數(shù)據(jù)間盡量少出現(xiàn)耦合關系 便于分別檢查和處理 2 在人工 靜態(tài) 檢查無誤后 才可以上機調試 通過上機發(fā)現(xiàn)錯誤稱動態(tài)檢查 在編譯時給出語法錯誤的信息 包括哪一行有錯以及錯誤類型 可以根據(jù)提示的信息具體找出程序中出錯之處并改正之 應當注意的是 有時提示的出錯行并不是真正出錯 的行 如果在提示出錯的行上找不到錯誤的話應當?shù)缴弦恍性僬?另外 有時提示出錯的類型并非絕對準確 由于出錯的情況繁多而且各種錯誤互有關聯(lián) 因此要善于分析 找出真正的錯誤 而不要只從字面意義上死摳出錯信息 鉆牛角尖 如果系統(tǒng)提示的出錯信息多 應當從上到下逐一改正 有時顯示出一大片出錯信息往往使人感到問題嚴重 無從下手 其實可能只有一二個錯誤 例如 對所用的變量未定義 編譯時就會對所有含該變量的語句發(fā)出出錯信息 只要加上一個變量定義 所有錯誤都消除了 3 在改正語法錯誤 包括 錯誤 error 和 警告 warning 后 程序經(jīng)過連接 link 就得到可 執(zhí)行的目標程序 運行程序 輸入程序所需數(shù)據(jù)

溫馨提示

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

最新文檔

評論

0/150

提交評論