



版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
編程精粹 Microsoft編寫優(yōu)質(zhì)無錯(cuò)C程序秘訣WritingCleanCode MicrosoftTechniquesforDevelopingBug-freeCPrograms原文書名:《WritingCleanCode MicrosoftTechniquesforDevelopingBug-freeCPrograms》Stevemaguire著MicrosoftPress出版整理:Solmyr!序、某些背景、命名約定、引言、第1、2、3、8章、后記、參考文獻(xiàn)iliad: 第4、5章lavos:第6章、附錄Awarz:第7章chief:附錄B、C校對(duì)、格式編排:SolmyrTOC\o"1-5"\h\z\o"CurrentDocument"序 3\o"CurrentDocument"命名約定 5\o"CurrentDocument"某些背景 6\o"CurrentDocument"引言 7\o"CurrentDocument"第1章 假想的編譯程序 12\o"CurrentDocument"第2章 自己設(shè)計(jì)并使用斷言 20\o"CurrentDocument"第3章 為子系統(tǒng)設(shè)防 44\o"CurrentDocument"第4章 對(duì)程序進(jìn)行逐條跟蹤 67\o"CurrentDocument"第5章 糖果機(jī)界面 75\o"CurrentDocument"第6章 風(fēng)險(xiǎn)事業(yè) 91\o"CurrentDocument"第7章 編碼中的假象 115\o"CurrentDocument"附錄A編碼檢查表 148\o"CurrentDocument"附錄B 內(nèi)存登錄例程 151\o"CurrentDocument"附錄C練習(xí)答案 159ノロ1己走向イ可方 182某些背景 III命名約定 IV引言 VI第1章 假想的編譯程序 1第2章 自己設(shè)計(jì)并使用斷言 8第3章 為子系統(tǒng)設(shè)防 31第4章 對(duì)程序進(jìn)行逐條跟蹤 53第5章 糖果機(jī)界面 60第6章 風(fēng)險(xiǎn)事業(yè) 75第7章 編碼中的假象 98\o"CurrentDocument"第8章 剩下的就是態(tài)度問題 115\o"CurrentDocument"后記 走向何方 129附錄A 編碼檢查表 130附錄B 內(nèi)存登錄例程 133附錄C 練習(xí)答案 140參考文獻(xiàn) 160(注:上述頁碼是以原書為基準(zhǔn),與本電子版本沒有什么關(guān)系)獻(xiàn)給我的妻子Beth,以及我的雙親Joseph和JuliaMaguire 為了他們的愛和支持序1986年,在為幾家小公司咨詢和工作了10年之后為了獲得編寫Macintosh應(yīng)用程序的經(jīng)驗(yàn),我特意到Microsoft公司工作,參加了Macintosh開發(fā)小組。這個(gè)小組負(fù)責(zé)Microsoft的圖形電子表格應(yīng)用程序的開發(fā)。當(dāng)時(shí),我還不能肯定想象的代碼是什么樣子的,我想也許應(yīng)該既引入入勝又雅致吧!但我看到的代碼卻很平常,與我以往見到的其它代碼沒有什么不同。要知道,Excel有一個(gè)相當(dāng)漂亮的用戶界面 它比當(dāng)時(shí)其它基于字符的電子表格軟件更容易使用,更加直觀。但使我感受更深的是產(chǎn)品中包含的一個(gè)多功能調(diào)試系統(tǒng)。該系統(tǒng)旨在自動(dòng)地問程序員和測(cè)試者進(jìn)行錯(cuò)誤報(bào)警。其工作方式非常象波音747駕駛倉內(nèi)向駕駛員報(bào)告故障的報(bào)警燈。該調(diào)試系統(tǒng)主要用來對(duì)代碼進(jìn)行監(jiān)視,它并不過多地對(duì)代碼進(jìn)行測(cè)試。雖然現(xiàn)在該調(diào)試系統(tǒng)采用的概念已不再新鮮了,但當(dāng)時(shí)它們的廣泛使用程度以及該系統(tǒng)有效的查錯(cuò)能力還是吸引了我,使我深受啟發(fā)。沒過多久,我就發(fā)現(xiàn)Microsoft的大部分項(xiàng)目都有多功能的內(nèi)部調(diào)試系統(tǒng),而Microsoft的程序員都高度重視代碼中的錯(cuò)誤及其產(chǎn)生原因。在做了兩年MacintoshExcel之后,我離開了該開發(fā)小組,去幫助另ー個(gè)代碼錯(cuò)誤數(shù)目超常的小組。在開發(fā)Excel的兩年中,我發(fā)現(xiàn)Microsoft雖然壯大了兩倍,但許多老項(xiàng)目組熟知的概念并沒有隨著公司的壯大而傳到新項(xiàng)目組。新程序員不象我加入Microsoft之前時(shí)的老程序員ー樣對(duì)容易引起錯(cuò)誤的編碼習(xí)慣特別重視,而只有一般的注意。在我轉(zhuǎn)到新項(xiàng)目組六個(gè)月之后,有一次我對(duì)ー個(gè)程序員伙伴提到:''應(yīng)該把編寫無錯(cuò)代碼的某些概念寫成文字,使那些原理能在新項(xiàng)目組傳開”。這時(shí)另一位程序員對(duì)我說:"你不要總是想著寫文檔,為什么你不把這一切都寫下來?為什么你不寫本書,問問Microsoft出版社是否愿意出版呢?畢竟這些信息不是誰的專利,其作用不過是為了使程序員更加重視錯(cuò)誤?!碑?dāng)時(shí)我對(duì)這個(gè)建議并沒有多想,主要原因是沒有時(shí)間,而且以前我也沒有寫過書。以前我所做過與寫書最有關(guān)系的事情,不過是在80年代初協(xié)助別人主辦Hi-Res雜志的程序設(shè)計(jì)專欄,這與寫書畢竟不是一回事。正如您所見到的,這本書還是寫出來了。理由很簡(jiǎn)單:1990年,由于錯(cuò)誤越來越多,Microsoft取消了一個(gè)尚未公布的產(chǎn)品?,F(xiàn)在,錯(cuò)誤越來越多已經(jīng)不是什么新鮮事情,Microsoft的幾個(gè)競(jìng)爭(zhēng)者都因此取消過ー些項(xiàng)目。但Microsoft因?yàn)檫@種原因取消項(xiàng)目,還是頭一次。最近,隨著接連不斷地出現(xiàn)產(chǎn)品錯(cuò)誤。管理人員終于開始叫嚷“受不了啦”,并采取了一系列的措施企圖將錯(cuò)誤率下降到原先的水平。盡管如此,仍然沒有人將這些錯(cuò)誤因由記錄下來。現(xiàn)在,Microsoft已經(jīng)比我剛進(jìn)公司時(shí)大了九倍。很難設(shè)想,倘若沒有準(zhǔn)確的指南,公司怎樣才能將出錯(cuò)率降低到原來的水平。尤其是在Windows和Macintosh的應(yīng)用越來越変雜的情況下,更是如此。以上就是我最終決定寫這本書的原因Microsoft出版社己經(jīng)同意出版這本書。情況就是這樣。我希望您會(huì)喜歡這本書,我力圖使本書不那么枯燥并盡量有趣。SteveMaguire西雅圖,華盛頓1992.10.22致謝我要感謝Microsoft出版社中幫助本書問世的所有人,尤其是在我寫作過程中始終手把手地教我的兩個(gè)人。首先我要感謝我的約稿編輯MikeHalvorson,他使我能夠按照自己的進(jìn)度完成了這本書,并且耐心地解答了我這個(gè)新作者提出的許多問題。我還要特別感謝我的責(zé)任編輯ErinO'Connor女士,她用了許多額外時(shí)間及早地對(duì)我寫出的章節(jié)提出了反饋意見。沒有他們的幫助,就不會(huì)有這本書。Erin還鼓勵(lì)我以幽默的風(fēng)格寫這本書。她對(duì)文中的小俏皮話發(fā)笑當(dāng)然不會(huì)使我不快。我還要感謝我的父親JosephMaguire是他在70年代中期把我引入早期的微機(jī)世界:Altair,IMSAI和Sol-20,使我與這一行結(jié)緣。我要感謝我于!981-83年在VaiparInternational工作時(shí)的伙伴EvanRosen,他是對(duì)我職業(yè)生涯最有影響的幾個(gè)人之一,他的知識(shí)以及洞察力在本書中都有所體現(xiàn)。還有PaulDavis,在過去的10年里,我與他在全國(guó)各地的許多項(xiàng)目中都有過愉快的合作,他也無疑的塑造了我的思維方式。最后感謝花時(shí)間閱讀本書草稿,并提供技術(shù)反饋意見的所有人。他們是:MarkGerber,MelissaGlerum,ChrisMason,DaveMoore,JohnRae-Grant和AlexTilles?特別感謝EricSchlegel和PaulDavis,他們不僅是本書草稿的評(píng)審者,而且在本書內(nèi)容的構(gòu)思上對(duì)我很有幫助。命名約定本書采用的命名約定和Microsoft所使用的“匈牙利式”命名約定差不多。該約定是由生于匈牙利布達(dá)佩斯的CharlesSimonyi開發(fā)的,它通過在數(shù)據(jù)和函數(shù)名中加入額外的信息以增進(jìn)程序員對(duì)程序的理解。例如:charch; /*所有的字符變量均以ch開始?/byteb; /?所有的字節(jié)均冠以b */long1; /?所有的長(zhǎng)字均冠以1 */對(duì)于指向某個(gè)數(shù)據(jù)類型的指針,可以先象上面那樣建立一個(gè)有類型的名字,然后給該名字加上前綴字母P:char*pch; /?指向ch的指針以p開始?/byte*pb; /?同理?/long*pl;void*pv; /?特意顯用的空指針*/char**ppch;/*指向字符指針的指針?/byte**ppb; /?指向字節(jié)指針的指針?/匈牙利式名字通常不那么好念,但在代碼中讀到它們時(shí),確實(shí)可以從中得到許多的信息。例如,當(dāng)你眼看到某個(gè)函數(shù)里有一個(gè)名為pch的變量時(shí),不用查看聲明就立即知道它是ー個(gè)指向字符的指針。為了使匈牙利式名字的描述性更強(qiáng).或者要區(qū)分兩個(gè)變量名,可以在相應(yīng)類型派生出的基本名字之后加上一個(gè)以大寫字母開頭的“標(biāo)簽二例如,strcpy函數(shù)有兩個(gè)字符指針參數(shù):ー個(gè)是源指針,另一個(gè)是目的指針。使用匈牙利式命名約定,其相應(yīng)的原型是:char*strcpy(char*pchTo,char*pchFrom); /?原型?/在上面的例子中,兩個(gè)字符指針有一個(gè)共同的特點(diǎn)——都指向以〇為結(jié)尾的C的字符串。因此在本書中,每當(dāng)用字符指針指向字符串時(shí),我們就用ー個(gè)更有意義的名子str來表示。因此,上述strcpy的原型則為:char*strcpy(char*strTo,char*strFrom) /?原型?/本書用到另ー個(gè)類型是ANSI標(biāo)準(zhǔn)中的類型size]。下面給出該類型的ー些典型用法:size_tsizeNew,sizeOld; /?原型?/void*malloc(size_tsize); /?原型?/void*realloc(void*pv,size_tsizeNew): /?原型?/函數(shù)和數(shù)組的命名遵循同樣的約定,名字由相應(yīng)的返回類型名開始,后跟ー個(gè)描述的標(biāo)簽。例如:ch=chLastKeyPressed: /*由變量得一字符?/ch=chInputBuffer[]; /?由數(shù)組得一字符?/ch=chReadKeyboard; /?由函數(shù)得一字符?/如果利用匈牙利式命名方法,mall、和reali~可以寫成如下形式:void*pvNewBlock(size_tsize); /?原型?/void*pvResizeBlock(void*pv,sizetsizeNew); /?原型?/由于匈牙利式命名方法旨在增進(jìn)程序員對(duì)程序的理解,所以大多數(shù)匈牙利式名字的長(zhǎng)度都要超過ANSI嚴(yán)格規(guī)定6個(gè)字母的限制。這就不妙,除非所用的系統(tǒng)是幾十年前設(shè)計(jì)的系統(tǒng),否則這6個(gè)字母的限制只當(dāng)是歷史的遺跡。以上內(nèi)容基本上沒有涉及到匈牙利式命名約定的細(xì)節(jié),所介紹的都是讀者理解本書中所用變量和函數(shù)名稱意義的必需內(nèi)容。如果讀者對(duì)匈牙利式命名約定的詳細(xì)內(nèi)容感興趣,可以參考本書末尾參考文獻(xiàn)部分列出的Simonyi的博士論文。某些背景本書用到了一些讀者可能不太熟悉的軟件和硬件系統(tǒng)的名稱。下面對(duì)其中最常見的幾個(gè)系統(tǒng)給出簡(jiǎn)單的描述MacintoshMacintosh是Apple公司的圖形窗口計(jì)算機(jī),公布于1984年。它是最先支持“所見即所得"擁戶界面的流行最廣的計(jì)算機(jī)。Windows Windows是Microsoft公司的圖形窗口操作系統(tǒng)。Microsoft公司1990年公布了Windows3.0?該版本明顯好于其早期版本。Excel Exce!是Microsoft公司的圖形電子表格軟件,1985年首次在Macintosh上公布,隨后在進(jìn)行了大量的重寫和排錯(cuò)工作后,被移植到Windows上。多年來MacintoshExcel和WindowsExcel共用ー個(gè)名字,但程序所用的代碼并不相同。在本書中,找多次提到曾經(jīng)當(dāng)過MacintoshExcel程序員這ー經(jīng)歷。但應(yīng)該說明的是,我的大部分工作是將WindowsExcel的代碼移到MacintoshExce!上或者是實(shí)現(xiàn)與WindowsExce!相似的特征。我與該產(chǎn)品現(xiàn)在的驚人成功并沒有特別的關(guān)系。我為MacintoshExce!所做的唯一真正重要的貢獻(xiàn)是說服Microsft放棄掉MacintoshExcel?而直接利用WindowsExcel的源代碼構(gòu)造Macintosh的產(chǎn)品。Macintosh2.2版是第一個(gè)基于WindowsExcel的版本,它享用了WindowsExcel80%的源代碼。這對(duì)MacintoshExcel的用戶意義重大,因?yàn)橛昧?.2版以后他們會(huì)感至MacintoshExcel的功能和質(zhì)量都有了一個(gè)很大的飛躍。Word Word是Microsoft公司的字處理應(yīng)用軟件。實(shí)際上,Word有三種版本:基于字符并在MS-DOS上運(yùn)行的DOS版;Macintosh版;Windows版。到目前為止,這三種版本的產(chǎn)品雖然是用不同的源代碼做出的,但它們非常相象,用戶改用任何ー個(gè)都不會(huì)有什么困難。由于Excel利用共享代碼獲得了成功,Microsoft已經(jīng)決定Word的高版本也將采用共享代碼進(jìn)行開發(fā)。80x86 80x86是MS-DOS和Windows機(jī)器常用的IntelCPU系列。680x0 680x0是各種Macintosh所用的MotorolaCPU系列。引言幾年前在一次偶然翻閱DonaldKnuth所著《TEX:TheProgram》ー書時(shí),序言中的ー段話深深觸動(dòng)了我:我確信TEX的最后ー個(gè)錯(cuò)誤已經(jīng)在1985年11月27日被發(fā)現(xiàn)并排除掉了。但是如果出于目前尚不知道的原因,TEX仍然潛伏有錯(cuò)誤,我非常愿意付給第一個(gè)發(fā)現(xiàn)者$20.48元。(這ー金額已是以前的兩倍。我打算在本年內(nèi)再增加一倍。你看我是多么自信!)我對(duì)Knuth是否曾經(jīng)付給某人$20.48甚至$40.96元不感興趣,這并不重要。重要的是他對(duì)他的程序所具有的那種自信。那么據(jù)你所知,究竟有多少程序員會(huì)嚴(yán)肅地聲稱他們的程序完全沒有錯(cuò)誤?又有多少敢把這一聲稱印刷在書上,并準(zhǔn)備為錯(cuò)誤的發(fā)現(xiàn)者付錢呢?如果程序員確信測(cè)試組已經(jīng)發(fā)現(xiàn)了所有的錯(cuò)誤,那么他也許敢作這種聲明。但這本身就是ー個(gè)問題。每當(dāng)代碼被打包裝送給程序經(jīng)銷商之前,人們?cè)谛厍皠澲謳е詈玫脑竿f:“希望測(cè)試已經(jīng)發(fā)現(xiàn)了所有的錯(cuò)誤”。這ー情景我已見過多次了。由于現(xiàn)代的程序員已經(jīng)放棄了對(duì)代碼進(jìn)行徹底測(cè)試的職責(zé),他們沒法知道代碼中是否有錯(cuò)。管理人員也不會(huì)公布測(cè)試情況,只是說:“別操那個(gè)心,測(cè)試人員會(huì)為你作好測(cè)試的”。更為微妙的是,管理人員希望程序員自己進(jìn)行代碼的測(cè)試。同時(shí),他們希望測(cè)試員作得更徹底些,因?yàn)楫吘惯@是他們的本職工作。正如你在本書中將會(huì)看到的那樣,編寫無錯(cuò)代碼的技術(shù)或許有幾百種,程序員能用,但測(cè)試人員卻無法使用,因?yàn)檫@些技術(shù)和代碼的編寫直接相關(guān)。兩個(gè)關(guān)鍵的問題本書介紹的所有決竅是當(dāng)發(fā)現(xiàn)錯(cuò)誤時(shí),不斷地就以下兩個(gè)問題追問自己的結(jié)果:怎樣才能自動(dòng)地査出這個(gè)錯(cuò)誤?怎樣才能避免這個(gè)錯(cuò)誤?第一個(gè)問題可能使讀者認(rèn)為本書是有關(guān)測(cè)試的書,其實(shí)不是。當(dāng)編輯程序發(fā)現(xiàn)語法錯(cuò)誤時(shí),它是在做測(cè)試嗎?不,不是。編輯程序只是在自動(dòng)地檢查代碼中的錯(cuò)誤。語法錯(cuò)誤只是程序員可以使用的自動(dòng)查錯(cuò)方法查出的一種最基本的錯(cuò)誤類型。本書將詳盡介紹自動(dòng)向程序員提示錯(cuò)誤的方法。編寫無錯(cuò)代碼的最好方法是把防上錯(cuò)誤放在第一位。關(guān)于這個(gè)問題,同樣也有許多的技巧。某些技巧與常用的編碼慣例有關(guān),但它們不是象“每個(gè)人都違背原則”或“沒有人違背該原則”這樣泛泛地考慮問題,而是對(duì)相應(yīng)的細(xì)節(jié)進(jìn)行詳細(xì)的討論。要記住,在任何時(shí)候跟在大多數(shù)人的后面常常是所能選擇的最壞一條路。因此在成為別人的追隨者之前一定要確定這樣做確實(shí)有意義,而且不要僅僅因?yàn)槠渌氖裁慈巳绱俗约阂踩绱?。本書的最后一章討論編寫無錯(cuò)代碼應(yīng)持正確態(tài)度的重要性。如果沒有正確的態(tài)度,發(fā)現(xiàn)錯(cuò)誤和防止錯(cuò)誤就好比在冬季大開著窗戶給房間加熱,雖然也能達(dá)到目的,但要浪費(fèi)大量的能量。本書除第4章和第8章以外都配有練習(xí)。但要注意,這些練習(xí)并不是要測(cè)驗(yàn)讀者對(duì)相應(yīng)內(nèi)容的理解。實(shí)際上更多的是作者想在該章的正文中闡述卻沒能放進(jìn)去的要點(diǎn)。其它的練習(xí)為的是讓讀者思考與該章內(nèi)容有關(guān)的ー些問題,打開思路,琢磨一下以前未曾考慮過的概念。無論哪種情況,都是想通過練習(xí)再補(bǔ)充一些新的技巧和信息,因此值得一讀。為使讀者了解作者的意圖,本書在附錄C中提供了所有練習(xí)的答案。大部分章節(jié)還給出了一些課題,但這些課題沒有答案,因?yàn)檫@種課題通常是任務(wù),而不是問題。規(guī)則或者建議本書的編排類似于BrianKernighan和P.J.Plauger所寫的程序設(shè)計(jì)經(jīng)典著作《TheElementsofProgrammingSytle》。該書出于WilliamStrunkJr.和E.B.White所寫的重要經(jīng)典著作《TheElementsofStyle》。這兩本書采用同樣的基本概念表達(dá)方法:給出ー個(gè)例子;指出該例子中的某些問題所在;用一般的準(zhǔn)則改進(jìn)該例子。確實(shí),這是個(gè)程式,而且是使讀者感到舒服的程式,因此本書同樣采用了這ー程式。作者力圖使本書讀起來是一種享受,盡管它有著公式的性質(zhì)。希望讀者會(huì)覺得本書很有趣。本書還給出ー些似乎不應(yīng)違背的“一般準(zhǔn)則”。我們的第一條準(zhǔn)則是;每條準(zhǔn)則都有例外由于準(zhǔn)則是用來說明一般情況的,所以本書一般并不指明準(zhǔn)則的例外情況,而把它留給讀者。我相信,當(dāng)讀者讀到某個(gè)準(zhǔn)則時(shí),肯定會(huì)懷疑道:“噢,當(dāng)……時(shí),不是這樣的……二如果某個(gè)人對(duì)你說:“不能闖紅燈”,雖然這是一條準(zhǔn)則,但你肯定能夠舉出ー種特殊情況,在這種情況下闖紅燈倒是個(gè)正確的行動(dòng)。這里關(guān)鍵是要記住準(zhǔn)則只是在一般情況下オ有意義,因此只有理由十分充足時(shí),オ可以違背準(zhǔn)則。關(guān)于本書代碼的說明本書的所有代碼都是按ANSIC寫的,并且通過了MS-DOS>MicrosoftWindows和AppleMacintosh上五個(gè)流行編譯程序的測(cè)試:MicrosoftC/C++7.0Microsoft公司TurboC/C++3.0Borland國(guó)際公司Aztec5.2Manx軟件系統(tǒng)公司MPWC3.2Apple計(jì)算機(jī)公司THINKC5.0Symantec公司還有一個(gè)問題:如果讀者想從本書中摘取代碼用在自己的程序中,那要小心。因?yàn)闉榱苏f明書中的論點(diǎn),許多例子都有錯(cuò)誤。另外,書中用到的函數(shù)雖然名字和功能都與ANSIC的標(biāo)準(zhǔn)庫函數(shù)相同,但已對(duì)相應(yīng)的界面進(jìn)行了一些小的修改。例如ANSI版memchr函數(shù)的界面是:void*memchr(constvoid*s,intc,size_tn);這里memchr的內(nèi)部將整數(shù)c當(dāng)作unsignedchar來處理。在本書的許多地方,讀者都會(huì)看到字符類型被顯式地聲明為unsignedchar,而不是int:void*memchr(constvoid*pv,unsignedcharch,size_tsize);ANSI標(biāo)準(zhǔn)將所有的字符變?cè)悸暶鳛閕nt,是為了保證其庫函數(shù)同樣可以用于ANSI標(biāo)準(zhǔn)之前編寫的非原型程序,這時(shí)程序使用extern聲明函數(shù)。由于在本書中只使用ANSIC,所以不必考慮這些向下兼容的細(xì)節(jié)而可以用更加精確的類型聲明以改進(jìn)程序的清晰程度并用原型進(jìn)行強(qiáng)類型檢查(詳見第1章)?!疤岬組acintosh了嗎?”出于某種原因,一本書如果沒有提到PDP11,Honeywell6000.當(dāng)然還有IBM360,就不會(huì)被認(rèn)真對(duì)待。因此,我在這里也提到了它們。僅此而已,讀者在本書中再也不會(huì)見到這些字眼、讀者見到最多的是MS-DOS,MicrosoftWindows,特別還有AppleMacintosh,因?yàn)榻陙砦乙恢睘檫@些系統(tǒng)編寫代碼。但是應(yīng)該注意,本書中的任何代碼都不受這些特定的系統(tǒng)約束。它們都是用通用的C編寫的,應(yīng)該能夠工作于任何的ANSIC編譯程序下。因此即使讀者使用的系統(tǒng)本書沒有提及,也不必?fù)?dān)心這些操作系統(tǒng)的細(xì)節(jié)會(huì)產(chǎn)生障礙。應(yīng)該提到的是在大多數(shù)的微機(jī)系統(tǒng)中,用戶都可以通過NULL指針進(jìn)行讀寫,破壞棧框架并在內(nèi)存甚至是其它應(yīng)用程序的內(nèi)存區(qū)中留下許多的無用信息,而硬件并沒有什么反應(yīng),聽任用戶為所欲為。之所以提到這一點(diǎn),是因?yàn)槿绻x者習(xí)慣于認(rèn)為通過NULL指針進(jìn)行寫操作會(huì)引起硬件故障的話,那么可能會(huì)對(duì)本書中的某些語句感到迷惑不解。遺憾的是,目前微機(jī)上的保護(hù)型操作系統(tǒng)仍不普及,破壞內(nèi)存的隱患必須通過硬件保護(hù)(通常它也不能提供充足的保護(hù))之外的方法才能發(fā)現(xiàn)。有錯(cuò)誤就有錯(cuò)誤不必為本書的讀者定義什么叫錯(cuò)誤,相信讀者都知道什么是錯(cuò)誤。但是錯(cuò)誤可以分為兩類:ー類是開發(fā)某ー功能時(shí)產(chǎn)生的錯(cuò)誤,另ー類是在程序員認(rèn)為該功能已經(jīng)開發(fā)完成之后仍然遺留在代碼中的錯(cuò)誤。例如在Microsoft中,每個(gè)產(chǎn)品都由一些絕不應(yīng)該含有錯(cuò)誤的原版源代碼構(gòu)成。當(dāng)程序員給產(chǎn)品增加新功能時(shí),并不直接改變相應(yīng)的原版源代碼,改變的是其副本。只有在這些改變已經(jīng)完成,并且程序員確信相應(yīng)代碼中已經(jīng)沒有錯(cuò)誤時(shí),オ將其合并到原版源代碼中。因此從產(chǎn)品質(zhì)量看,在實(shí)現(xiàn)指定功能過程中不論產(chǎn)生多少個(gè)錯(cuò)誤都沒有關(guān)系,只要這些錯(cuò)誤在相應(yīng)代碼被并入原版源代碼之前被刪除掉就行。所有的錯(cuò)誤都有害,但損害產(chǎn)品最危險(xiǎn)的錯(cuò)誤是己經(jīng)進(jìn)入原版源代碼中的錯(cuò)誤。因此,本書中提到的錯(cuò)誤指的就是這些已經(jīng)進(jìn)入原版源代碼中的錯(cuò)誤。作者并不指望程序員在鍵入計(jì)算機(jī)之前總是寫出沒有錯(cuò)誤的代碼,但確信防止錯(cuò)誤侵入原版源代碼是完全可能的。尤其是程序員在使用了本書提供的秘訣之后,更是如此。第1章假想的編譯程序讀者可以考慮一下倘若編譯程序能夠正確地指出代碼中的所有問題,那相應(yīng)程序的錯(cuò)誤情況會(huì)怎樣?這不單指語法錯(cuò)誤,還包括程序中的任何問題,不管它有多么隱蔽。例如,假定程序中有‘‘差1"錯(cuò)誤,編譯程序可以采用某種方法將其査出,并給出如下的錯(cuò)誤信息>1ine23:while(i<-j)offbyoneerror:thisshouldbe'ぐ又如,編譯程序可以發(fā)現(xiàn)算法中有下面的錯(cuò)誤:>line42:intitoa(inti,char*str)algorithmerror:itoafailswheniis-32768再如,當(dāng)出現(xiàn)了參數(shù)傳遞錯(cuò)誤時(shí),編譯程序可以給出如下的錯(cuò)誤信息:>line318:strCopy=memcpy(malloc(length),str,length);Invalidargument:memcpyfailswhenmallocreturnsNULL好了,要求編譯程序能夠做到這ー程度似乎有點(diǎn)過分。但如編譯程序真能做到這些,可以想象編寫無錯(cuò)程序會(huì)變得多么容易。那簡(jiǎn)直是小事ー樁,和當(dāng)前程序員的一般作法真沒法比。假如在間諜衛(wèi)星上用攝像機(jī)對(duì)準(zhǔn)某個(gè)典型的軟件車間.就會(huì)看到程序員們正弓著身子趴在鍵盤上跟蹤錯(cuò)誤;旁邊,測(cè)試者正在對(duì)剛作出的內(nèi)部版本發(fā)起攻擊,輪番轟炸式地輸入人量的數(shù)據(jù)以求找出新的錯(cuò)誤。你還會(huì)發(fā)現(xiàn),測(cè)試員正在檢查老版本的錯(cuò)誤是否溜進(jìn)了新版本??梢酝葡耄@種查錯(cuò)方法比用上面的假想編譯程序進(jìn)行查錯(cuò)要花費(fèi)大得多的工作量、確實(shí)如此,而且它還要有點(diǎn)運(yùn)氣。運(yùn)氣?是的,運(yùn)氣。測(cè)試者之所以能夠發(fā)現(xiàn)錯(cuò)誤,不正是因?yàn)樗⒁獾搅酥T如某個(gè)數(shù)不對(duì)、某個(gè)功能沒按所期望的方式工作或者程序癱瘓這些現(xiàn)象嗎?再看看上面的假想編譯程序給出的上述錯(cuò)誤:程序雖然有了“差1”錯(cuò)誤,但如果它仍能工作,那么測(cè)試者能看得出來嗎?就算看得出來,那么另外兩個(gè)錯(cuò)誤呢?這聽起來好象很可怕但測(cè)試人員就是這樣做的大量給程序輸入數(shù)據(jù),希望潛在的錯(cuò)誤能夠亮相?!班蓿?我們測(cè)試人員的工作可不這么簡(jiǎn)單,我們還要使用代碼覆蓋工具、自動(dòng)的測(cè)試集、隨機(jī)的“猴”程序、抽點(diǎn)打印或其他什么的”。也許是這樣,但還是讓我們來看看這些工具究竟做了些什么吧!覆蓋分析工具能夠指明程序中哪些部分未被測(cè)試到,測(cè)試人員可以使用這一信息派生出新的測(cè)試用例。至于其它的工具無非都是“輸入數(shù)據(jù)、觀察結(jié)果”這ー策略的自動(dòng)化。請(qǐng)不要產(chǎn)生誤解,我并不是說測(cè)試人員的所作所為都是錯(cuò)誤的。我只是說利用黑箱方法所能做的只是往程序里填數(shù)據(jù),并看它彈出什么。這就好比確定一個(gè)人是不是瘋子ー樣。問ー些問題,得到回答后進(jìn)行判斷。但這樣還是不能確定此人是不是瘋子。因?yàn)槲覀儧]法知道其頭腦中在想些什么。你總會(huì)這樣地問自己:“我問的問題夠嗎?我問的問題對(duì)嗎……”。因此,不要光依賴黑箱測(cè)試方法。還應(yīng)該試著去模仿前面所講的假想編譯程序,來排除運(yùn)氣對(duì)程序測(cè)試的影響,自動(dòng)地抓住錯(cuò)誤的每個(gè)機(jī)會(huì)。考慮一下所用的語言你最后一次看推銷字處理程序的廣告是什么時(shí)候?如果那個(gè)廣告是麥迪遜大街那伙人寫的,它很可能是這么說:"無論是給孩子們的老師寫便條還是為卜期的《GreatAmericanNovel》撰稿,WordSmasher都能行,毫不費(fèi)勁!WordSmasher配備了令人吃驚的23300〇字的拼寫字典,足足比同類產(chǎn)品多51000個(gè)字。它可以方便地找岀樣稿中的打字錯(cuò)誤。趕快到經(jīng)銷商那里去買一份拷貝。WordSmasher是從圓珠筆問世以來最革命性的書寫工具!”。用戶經(jīng)過不斷地市場(chǎng)宣傳熏陶,差不多都相信拼寫字典越大越好,但事實(shí)并非如此。象em、abel和si這些詞,在任何一本簡(jiǎn)裝字典中都可以査到、但在me、able和is如此常見的情況下您還想讓拼寫檢查程序認(rèn)為em、abel和si也是拼寫正確的詞嗎?如果是,那么當(dāng)你看到我寫的suing時(shí),其本意很可能是與之風(fēng)馬牛不相及的using。問題不在于suing是不是ー個(gè)真正的詞而在于它在此處確實(shí)是個(gè)錯(cuò)誤。幸運(yùn)的是,某些質(zhì)量比較高的拼寫檢查程序允許用戶刪去象em這類容易引起麻煩的詞。這樣一來,拼寫檢查程序就可以把原來合法的單詞看成是拼寫錯(cuò)誤。好的編譯程序也應(yīng)該能夠這樣 可以把屢次出錯(cuò)的合法的C習(xí)慣用法看成程序中的錯(cuò)誤。例如,這類編譯程序能夠檢査出以下while循環(huán)錯(cuò)放了一個(gè)分號(hào):/*memcpy復(fù)制ー個(gè)不重卷的內(nèi)存塊?/void*memcpy(void*pvTo,void*pvFrom,size_tsize)(byte*pbTo=(byte*)pvTo:byte*pbFrom=(byte*)pvFrom;while(size—>0);*pbTo++=*pbFrom++;return(pvTo);}我們從程序的縮進(jìn)情況就可以知道while表達(dá)式后由的分號(hào)肯定是個(gè)錯(cuò)誤,但編譯程序卻認(rèn)為這是ー個(gè)完全合法的while語句,其循環(huán)體為空語句。由于有時(shí)需要空語句,有時(shí)不需要空語句,所以為了查出不需要的空語句,編譯程序常常在遇到空語句時(shí)給出一條可選的警告信息,自動(dòng)警告你可能出了上面的錯(cuò)誤。當(dāng)確定需要用空語句時(shí),你就用。但最好用NULL使其明顯可見。例如:char*strcpy(char*pchTo,char*pchFrom)char*pchStart=pchTo;while(*pchTo++=*pchFrom++)NULL;Return(pchStart);}由于NULL是個(gè)合法的C表達(dá)式,所以這個(gè)程序沒有問題。使用NULL的更大好處在于編譯程序不會(huì)為NULL語句生成任何的代碼,因?yàn)镹ULL只是個(gè)常量。這樣,編譯程序接受顯式的NULL語句,但把隱式空語句自動(dòng)地當(dāng)作錯(cuò)誤標(biāo)出。在程序中只允許使用ー種形式的空語句,如同為了保持文字的一致性,文中只想使用zero的ー種復(fù)數(shù)形式zeroes,因此要從拼寫字典中刪除另ー種復(fù)數(shù)形式zeros.,另ー個(gè)常見的問題是無意的賦值。C是一個(gè)非常靈活的語言,它允許在任何可以使用表達(dá)式的地方使用賦值語句。因此如果用戶不夠謹(jǐn)慎,這種多余的靈活性就會(huì)使你犯錯(cuò)誤。例如,以下程序就出現(xiàn)了這種常見的錯(cuò)誤:if(ch='\t')ExpandTab();雖然很清楚該程序是要將ch與水平制表符作比較,但實(shí)際上卻成了對(duì)ch的賦值。對(duì)于這種程序,編譯程序當(dāng)然不會(huì)產(chǎn)生錯(cuò)誤,因?yàn)榇a是合法的C。某些編譯程序允許用戶在&&和 表達(dá)式以及if、for和while構(gòu)造的控制表達(dá)式中禁止使用簡(jiǎn)單賦值,這樣就可以幫助用戶查出這種錯(cuò)誤。這種做法的基本依據(jù)是用戶極有可能在以上五種情況下將等號(hào)==偶然地健入為賦值號(hào)=。這種選擇項(xiàng)并不妨礙用戶作賦值,但是為了避免產(chǎn)生警告信息,用戶必須再拿別的值,如零或空字符與賦值結(jié)果做顯式的比較。因此,對(duì)于前面的strcpy例子,若循環(huán)寫成:while(*pchTo++=*pchFrom++)NULL;編譯程序會(huì)產(chǎn)生警告信息ー所以耍寫成;while((*pchTo++=*pchFrom++)!=‘、0’)NULL;這樣做有兩個(gè)好處。第一,現(xiàn)代的商用級(jí)編譯程序不會(huì)為這種冗余的比較產(chǎn)生額外的代碼,可以將其優(yōu)化掉。因此,提供這種警告選擇項(xiàng)的編譯程序是可以信賴的。第二,它可以少冒風(fēng)險(xiǎn),盡管兩種都合法,但這是更安全的用法。另ー類錯(cuò)誤可以被歸入“參數(shù)錯(cuò)誤”之列。例如,多年以前,當(dāng)我正在學(xué)C語言時(shí),曾經(jīng)這樣調(diào)用過fputc:fprintf(stderr,wUnabletoopenfile%s.\n”,filename);fputc(stderr,'\n');這ー程序看起來好象沒有問題,但fputc的參數(shù)次序錯(cuò)了。不知道為什么,我一直認(rèn)為流指針(stderr)總是這類流函數(shù)的第一個(gè)參數(shù)。事實(shí)并非如此,所以我常常給這些函數(shù)傳遞過去許多沒用的信息。幸好ANSIC提供了函數(shù)原型,能在編譯時(shí)自動(dòng)地查出這些錯(cuò)誤。由于ANSIC標(biāo)準(zhǔn)要求每個(gè)庫函數(shù)都必須有原型所以在stdio.h頭文件中能夠找到fputc的原型。fputc的原型是:intfputc(intc,FILE*stream);如果在程序中include了stdio.h,那么在調(diào)用fputc時(shí),編譯程序會(huì)根據(jù)其原型對(duì)所傳遞的每個(gè)參數(shù)進(jìn)行比較。如果二者類型不同,就會(huì)產(chǎn)生編譯錯(cuò)誤。在上面的錯(cuò)誤例于中,因?yàn)樵趇nt的位置上傳遞了FILE?類型的參數(shù),所以利用原型可以自動(dòng)地發(fā)現(xiàn)前ー個(gè)fputc的錯(cuò)誤。ANSIC雖然要求標(biāo)準(zhǔn)的庫函數(shù)必須有原型,但并不要求用戶編寫的函數(shù)也必須有原型。嚴(yán)格地說,它們可以有原型,也可以沒有原型。如果用戶想要檢查出自己程序中的調(diào)用錯(cuò)誤,必須自己建立原型,并隨時(shí)使其與相應(yīng)的函數(shù)保持一致。最近我聽到程序員在抱怨他們必須對(duì)函數(shù)的原型進(jìn)行維護(hù)。尤其是剛從傳統(tǒng)C項(xiàng)目轉(zhuǎn)到ANSIC項(xiàng)目時(shí),這種抱怨更多。這種抱怨是有一定理由的,但如果不用原型,就不得不依賴傳統(tǒng)的測(cè)試方法來查出程序中的調(diào)用錯(cuò)誤。你可以捫心自問,究竟哪個(gè)更重要,是減少ー些維護(hù)工作量,還是在編譯時(shí)能夠查出錯(cuò)誤?如果你還不滿意,請(qǐng)?jiān)倏紤]一下利用原型可以生成質(zhì)量更好的代碼這一事實(shí)。這是因?yàn)?ANSIC標(biāo)準(zhǔn)使得編譯程序可以根據(jù)原型信息進(jìn)行相應(yīng)的優(yōu)化。在傳統(tǒng)C中,對(duì)于不在當(dāng)前正被編譯的文件中的函數(shù),編譯程序基本上得不到關(guān)于它的信息。盡管如此,編譯程序仍然必須生成對(duì)這些函數(shù)的調(diào)用,而且所生成的調(diào)用必須奏效。編譯程序?qū)崿F(xiàn)者解決這個(gè)問題的辦法是使用標(biāo)準(zhǔn)的調(diào)用約定。這一方法雖然奏效,但常常意味著編譯程序必須生成額外的代碼,以滿足調(diào)用約定的要求。但如果使用了“要求所有函數(shù)都必須有原型”這ー編譯程序提供的警告選擇項(xiàng),由于編譯程序了解程序中每個(gè)函數(shù)的參數(shù)情況,所以可以為不同的函數(shù)選擇它認(rèn)為最有效率的調(diào)用約定??照Z句、錯(cuò)誤的賦值以及原型檢查只是許多C編譯程序提供的選擇項(xiàng)中的ー小部分內(nèi)容,實(shí)際上常常還有更多的其它選擇項(xiàng)。這里的要點(diǎn)是:用戶可以選擇的編譯程序警告設(shè)施可以就可能的錯(cuò)誤向用戶發(fā)出警告信息,其工作的方式非常類似于拼寫檢查程序?qū)赡艿钠磳戝e(cuò)誤的處理PeterLynch,據(jù)說是80年代最好的合股投資公司管理者,他曾經(jīng)說過:投資者與賭徒之間的區(qū)別在于投資者利用每一次機(jī)會(huì),無論它是多么小,去爭(zhēng)取利益;而賭徒則只靠運(yùn)氣。用戶應(yīng)該將這一概念同樣應(yīng)用于編程活動(dòng),選擇編譯程序的所有可選警告設(shè)施,并把這些措施看成是ー種無風(fēng)險(xiǎn)高償還的程序投資。再不要問:“應(yīng)該使用這ー警告設(shè)施嗎?而應(yīng)該問:“為什么不使用這ー警告設(shè)施呢?エ要把所有的警告開關(guān)都打開,除非有極好的理由オ不這樣做。使用編譯程序所有的可選警告設(shè)施增強(qiáng)原型的能力不幸的是,如果函數(shù)有兩個(gè)參數(shù)的類型相同,那么即使在調(diào)用該函數(shù)時(shí)互換了這兩個(gè)參數(shù)的位置,原型也查不出這ー調(diào)用錯(cuò)誤。例如,如果函數(shù)memchr的原型是:void*memchr(constvoid*pv,intch,intsize);那么在調(diào)用該函數(shù)時(shí),即使互換其字符ch和大小size參數(shù),編譯程序也不會(huì)發(fā)出警告信息。但是如果在相應(yīng)界面和原型中使用了更加精確的類型,就可以增強(qiáng)原型提供的錯(cuò)誤檢查能カ。例如,如果有了下面的原型:void*memchr(constvoid*pv,unsignedcharch,size_tsize);那么在調(diào)用該函數(shù)時(shí)弄反了其字符ch和大小size參數(shù),編譯程序就會(huì)給出警告錯(cuò)誤.在原型中使用更精確類型的抉陷是常常必須進(jìn)行參數(shù)的顯式類型轉(zhuǎn)換,以消除類型不匹配的錯(cuò)誤,即使參數(shù)的次序正確。lint并不那么差另ー種檢查錯(cuò)誤更詳細(xì)、更徹底的方法是使用lint,這種方法幾乎不費(fèi)什么事。最初,lint這個(gè)工具用來掃描C源文件并對(duì)源程序中不可移植的代碼提出警告。但是現(xiàn)在大多數(shù)lint實(shí)用程序已經(jīng)變得更加嚴(yán)密,它不但可以檢查出可移植性問題,而且可以檢查出那些雖然可移植并且完全合乎語法但卻很可能是錯(cuò)誤的特性,上一節(jié)那些可疑的錯(cuò)誤就屬于這ー類。不幸的是,許多程序員至今仍然把lint看作是一個(gè)可移植性的檢查程序,認(rèn)為它只能給出一大堆無關(guān)的警告信息??傊?lint得到了不值得麻煩的名聲。如果你也是這樣想的程序員,那么你也許應(yīng)該重新考慮你的見解。想ー想究竟是哪ー種工具更加接近于前文所述的假想編譯程序,是你正使用的編譯程序,還是lint?實(shí)際上,一旦源程序變成了沒有l(wèi)int錯(cuò)誤的形式,繼續(xù)使其保持這種狀態(tài)是很容易做到的。只要對(duì)所改變的部分運(yùn)行l(wèi)int,沒有錯(cuò)誤之后再把其并入到原版源代碼中即可。利用這種方法,并不要進(jìn)行太多的考慮,只要經(jīng)過ー、二周就可以寫出沒有l(wèi)int錯(cuò)誤的代碼。在達(dá)到這個(gè)程度時(shí),就可以得到lint帶來的各種好處了。使用lint來查出編譯程序漏掉的錯(cuò)誤但我做的修改很平常一次在同本書的ー個(gè)技術(shù)評(píng)審者共進(jìn)午餐時(shí),他問我本書是否打算包括ー節(jié)單元測(cè)試方面的內(nèi)容。我回答說:“不”。因?yàn)楸M管単元測(cè)試也與無錯(cuò)代碼的編寫有關(guān),但它實(shí)際上屬于另ー個(gè)不同的類別,即如何為程序編寫測(cè)試程序。他說:“不,你誤解了。我的意思是你是否打算指出在將新做的修改并入原版源代碼之前,程序員應(yīng)該實(shí)際地進(jìn)行相應(yīng)的單元測(cè)試。我的小組中的一位程序員就是因?yàn)樵谶M(jìn)行了程序的修改之后沒有進(jìn)行相應(yīng)的単元測(cè)試,使ー個(gè)錯(cuò)誤進(jìn)入到我們的原版源代碼中?!边@使我感到很驚奇。因?yàn)樵贛icrosoft,大多數(shù)項(xiàng)目負(fù)責(zé)人都要求程序員在合并修改了的源代碼之前,要進(jìn)行相應(yīng)的單元測(cè)試。“你沒問他為什么不做單元測(cè)試嗎?”,我問道。我的朋友從餐桌上抬起頭來對(duì)我說:“他說他并沒有編寫任何新的代碼,只是對(duì)現(xiàn)有代碼進(jìn)行了某些移動(dòng)。他說他認(rèn)為沒必要再進(jìn)行單元測(cè)試ニ這種事情在我的小組中也曾經(jīng)發(fā)生過。它使我想起曾經(jīng)有一個(gè)程序員在進(jìn)行了修改之后,甚至沒有再編譯一次就把相應(yīng)的代碼并入了原版源代碼中。當(dāng)然,我發(fā)現(xiàn)了這ー問題,因?yàn)槲以趯?duì)原版源代碼進(jìn)行編譯時(shí)產(chǎn)生了錯(cuò)誤。當(dāng)我問這個(gè)程序員怎么會(huì)漏掉這個(gè)編譯錯(cuò)誤,他說:“我做的修改很平常,我認(rèn)為不會(huì)出錯(cuò)”,但他錯(cuò)了。這些錯(cuò)誤本來都應(yīng)該不會(huì)進(jìn)入原版源代碼中,因?yàn)槎叨伎梢詭缀鹾敛毁M(fèi)カ地被查岀來。為什么程序員會(huì)犯這種錯(cuò)誤呢?是他們過高地估計(jì)了自己編寫正確代碼的能力。有時(shí),似乎可以跳過ー些設(shè)計(jì)用來避免程序出錯(cuò)的步驟,但走捷徑之時(shí),就是麻煩將至之日。我懷疑會(huì)有許多的程序員甚至沒有對(duì)相應(yīng)的代碼進(jìn)行編譯,就‘‘完成”了某ー功能。我知道這只是偶然情況,但繞過單元測(cè)試的趨勢(shì)正在變強(qiáng),尤其是作簡(jiǎn)單的改動(dòng)。如果你發(fā)現(xiàn)自己正打算繞過某個(gè)步驟。而它恰恰可以很容易地用來查錯(cuò),那么一定要阻止自己繞過。相反,要利用所能得到的每個(gè)工具進(jìn)行査錯(cuò)。此外,單元測(cè)試雖然意味著查錯(cuò),但如果你根本就不進(jìn)行單元測(cè)試也是枉然。如果有單元測(cè)試,就進(jìn)行單元測(cè)試小結(jié)你認(rèn)識(shí)哪個(gè)程序員寧愿花費(fèi)時(shí)間去跟蹤排錯(cuò),而不是編寫新的代碼?我肯定有這種程序員,但至今我還沒有見過ー個(gè)。對(duì)于我認(rèn)識(shí)的程序員,如果你答應(yīng)他們?cè)俨挥酶櫹漏`個(gè)錯(cuò)誤,他們會(huì)寧愿ー輩子放棄享用中國(guó)菜。當(dāng)你寫程序時(shí),要在心中時(shí)刻牢記著假想編譯程序這一概念,這樣就可以毫不費(fèi)カ或者只費(fèi)很少的カ氣利用每個(gè)機(jī)會(huì)抓住錯(cuò)誤。要考慮編譯程序產(chǎn)生的錯(cuò)誤、lint產(chǎn)生的錯(cuò)誤以及單元測(cè)試失敗的原因。雖然使用這些工具要涉及到很多的特殊技術(shù),但如果不花這么多的功夫,那產(chǎn)品中會(huì)有多少個(gè)錯(cuò)誤?如果想要快速容易地發(fā)現(xiàn)錯(cuò)誤,就要利用工具的相應(yīng)特性對(duì)錯(cuò)誤進(jìn)行定位。錯(cuò)誤定位得越早,就能夠越早地投身于更有興趣的工作。要點(diǎn):消除程序錯(cuò)誤的最好方法是盡可能早、盡可能容易地發(fā)現(xiàn)錯(cuò)誤,要尋求費(fèi)カ最小的自動(dòng)查錯(cuò)方法。努力減少程序員查錯(cuò)所需的技巧??梢赃x擇的編譯程序或lint警告設(shè)施并不要求程序員要有什么查錯(cuò)的技巧。在另ー個(gè)極端,高級(jí)的編碼方法雖然可以查出或減少錯(cuò)誤,但它們也要求程序員要有較多的技巧,因?yàn)槌绦騿T必須學(xué)習(xí)這些高級(jí)的編碼方法。練習(xí):1)假如使用了禁止在while的條件部分進(jìn)行賦值的編譯程序選擇項(xiàng),為什么可以查出ド面代碼中的運(yùn)算優(yōu)先級(jí)錯(cuò)誤?While(ch=getchar()!=EOF)2)看看你怎樣使用編譯程序查出無意使用的空語句和賦值語句。值得推薦的辦法是進(jìn)行相應(yīng)的選擇,使編譯程序能夠?qū)ο铝谐R妴栴}產(chǎn)生警告信息。怎樣才能消除這些警告信息呢?if(flight==063)?這里程序員的本意是對(duì)63號(hào)航班進(jìn)行測(cè)試,但因?yàn)榍懊娑嗔艘粋€(gè)〇使063成了ハ進(jìn)制數(shù)。結(jié)果變成對(duì)51號(hào)航班進(jìn)行測(cè)試。If(pb!=NULL&pb!=Oxff)〇這里不小心把&&鍵入為&,結(jié)果即使pb等于NULL還會(huì)執(zhí)行?pb!=Oxffoquot=numer/*pdenom。這里無意間多了個(gè)?號(hào)結(jié)果使/?被解釋為注釋的開始。word=bHigh?8+bLow。由于出現(xiàn)了運(yùn)算優(yōu)先級(jí)錯(cuò)誤,該語句被解釋成了:word=bHigh?(8+bLow)3)編譯程序怎樣才能對(duì)“沒有與之配對(duì)的else”這ー錯(cuò)誤給出警告?用戶怎樣消除這ー警告?4)再看一次下面的代碼:if(ch=='\t')ExpandTab():除禁止在if語句中使用簡(jiǎn)單賦值的方法之外,能夠查出這個(gè)錯(cuò)誤的另ー種眾所周知的方法是把賦值號(hào)兩邊的操作數(shù)顛倒過來:if('\t'==ch)ExpandTab():這樣如果應(yīng)該鍵入==時(shí)健入了=,編譯程序就會(huì)報(bào)錯(cuò),因?yàn)椴辉试S對(duì)常量進(jìn)行賦值。這個(gè)辦法徹底嗎?為什么它不象編譯程序開關(guān)自動(dòng)化程度那么高?為什么新程序員會(huì)用賦值號(hào)代替等號(hào)?5)C的預(yù)處理程序也可能引起某些意想不到的結(jié)果。例如,宏UINT_MAX定義在limit,h中,但假如在程序中忘了include這個(gè)頭文件,下面的偽指令就會(huì)無聲無息地失敗,因?yàn)轭A(yù)處理程序會(huì)把預(yù)定義的UINT_MAX替換成0:#ifUINT_MAX>65535uttendif怎樣使預(yù)處理程序報(bào)告出這ー錯(cuò)誤?課題:為了減輕維護(hù)原型的工作量,某些編譯程序會(huì)在編譯時(shí)自動(dòng)地為所編譯的程序生成原型。如果你用的編譯程序沒有提供這ー選擇項(xiàng),自己寫ー個(gè)使用程序來完成這ー工作。為什么標(biāo)準(zhǔn)的編碼約定可以使這個(gè)使用程序的編寫變得相對(duì)容易?課題:如果你用的編譯程序還不支持本章(包括練習(xí))中提及的警告設(shè)施,那么促進(jìn)相應(yīng)的制造商支持這些設(shè)施,另外要敦促他們除了允許用戶設(shè)定或者取消對(duì)某些類錯(cuò)誤的檢查之外,還要提供有選擇地設(shè)定或取消一些特定的警告設(shè)施,為什么耍這樣做呢?第2章 自己設(shè)計(jì)并使用斷言利用編譯程序自動(dòng)?xùn)隋e(cuò)固然好,但我敢說只要你觀察一下項(xiàng)目中那些比較明顯的錯(cuò)誤,就會(huì)發(fā)現(xiàn)編譯程序所查出的只是其中的ー小部分。我還敢說,如果排除掉了程序中的所有錯(cuò)誤那么在大部分時(shí)間內(nèi)程序都會(huì)正確工作。還記得第1章中的下面代碼嗎?strCopy=memcpy(mal1oc(1ength),str,length);該語句在多數(shù)情況下都會(huì)工作得很好,除非malloc的調(diào)用產(chǎn)生失敗。當(dāng)malloc失敗時(shí),就會(huì)給memcpy返回ー個(gè)NULL指針。由于memcpy處理不了NULL指針,所以出現(xiàn)了錯(cuò)誤。如果你很走運(yùn),在交付之前這個(gè)錯(cuò)誤導(dǎo)致程序的癱瘓,從而暴露出來。但是如果你不走運(yùn),沒有及時(shí)地發(fā)現(xiàn)這個(gè)錯(cuò)誤,那某位顧客
溫馨提示
- 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. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年食堂工作人員聘用及勞動(dòng)合同續(xù)簽協(xié)議
- 二零二五年度水利工程安全驗(yàn)收與監(jiān)管協(xié)議
- 二零二五年度文學(xué)作品改編權(quán)購買合同范本
- 二零二五年度小企業(yè)實(shí)習(xí)設(shè)計(jì)師勞動(dòng)合同模板
- 2025至2030年中國(guó)離心式渣漿泵數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 二零二五年度收養(yǎng)協(xié)議書匯編:兒童收養(yǎng)后的教育與成長(zhǎng)規(guī)劃
- 二零二五年度朋友絕交協(xié)議附贈(zèng)雙方心理咨詢服務(wù)協(xié)議
- 短視頻在社交媒體品牌推廣中的應(yīng)用報(bào)告
- 社交媒體中電信詐騙的演變與對(duì)策
- 2025至2030年中國(guó)直讀式測(cè)鈣儀數(shù)據(jù)監(jiān)測(cè)研究報(bào)告
- 鎮(zhèn)鄉(xiāng)自然資源規(guī)劃所工作職責(zé)
- 年終獎(jiǎng)計(jì)算方案
- 模擬藥房實(shí)訓(xùn)總結(jié)報(bào)告
- 人工智能在智能運(yùn)維中的應(yīng)用
- 《腦科學(xué)基礎(chǔ)知識(shí)》課件
- 成人四肢血壓測(cè)量的中國(guó)專家共識(shí)
- 榮昌壩扶壁式擋土墻施工方案1
- 幼兒園多媒體課件設(shè)計(jì)與制作第2版(高職學(xué)前教育專業(yè))全套教學(xué)課件
- 動(dòng)力電池包pack控制計(jì)劃
- 01SS105給排水常用儀表及特種閥門安裝圖集
- 南寧水療市場(chǎng)調(diào)研分析報(bào)告
評(píng)論
0/150
提交評(píng)論