田納西.威廉斯(Tennessee Williams):
我們彼此猜忌,實(shí)屬情非得已。這是我們防止背叛的唯一武器。
一、編寫優(yōu)秀代碼:
《程序設(shè)計原理》中M.A.Jackson寫道:
軟件工程師的智慧,就在于他們是否開始意識到:使程序能用和使程序正確,這兩者之間有什么樣的差別。
- 可用的代碼:提供常規(guī)輸入集,代碼給出常規(guī)輸出;一旦有意外輸入,代碼崩潰。
- 正確的代碼:對于所有輸入集,有正確的輸出;
- 優(yōu)秀的代碼:一定是正確的代碼;邏輯容易理解;代碼自然;容易維護(hù)。
產(chǎn)品級的代碼,面對不常見的輸入時不會崩潰,也不會出現(xiàn)錯誤的結(jié)果;同時,滿足其他要求,包括『可重入』、『線程安全』、時間約束等。
二、一種防止代碼漏洞百出的手段——防御性編程
墨菲定律(Murphy's Law):
凡是可能出錯的事,準(zhǔn)會出錯。
防御性編程通過預(yù)見到(至少預(yù)先推測到)問題所在,斷定代碼中每個階段可能出現(xiàn)的問題,并做出相應(yīng)的防范措施,來防止這類意外產(chǎn)生。
三、防御性編程技巧
使用好的編碼風(fēng)格和合理的設(shè)計
命名合理;審慎地使用括號;
編碼前,設(shè)計好接口避免閃電式編程
每敲一個字,都想清楚你輸入的是什么
進(jìn)入下一個環(huán)節(jié)之前,完成上一個代碼段的所有任務(wù)不要相信任何人
任何人,包括你自己,都可能把缺陷引入你的程序邏輯中
用懷疑的眼光審視所有的輸入和所有的輸出,直到確保正確為止。
在程序各處添加安全檢查保持代碼簡單
將復(fù)雜的代數(shù)運(yùn)算拆分為一些列單獨(dú)的語句,使邏輯清晰。不要讓任何人做他們不該做的修補(bǔ)工作
面向?qū)ο笳Z言中,將屬性設(shè)置為private,并提供public函數(shù)操作它們;
如果變量可以聲明為函數(shù)內(nèi)局部變量,不要在文件范圍內(nèi)聲明;
如果變量可以聲明為循環(huán)體內(nèi)局部變量,不要在函數(shù)范圍內(nèi)聲明。編譯時開啟所有警告開關(guān)
編譯器的警告能捕捉到許多愚蠢的編碼錯誤;使用靜態(tài)分析工具
編譯器只能對代碼進(jìn)行有限的靜態(tài)分析;
靜態(tài)分析工具:C語言的lint;.NET的FxCop使用安全的數(shù)據(jù)結(jié)構(gòu)
最常見的安全隱患為緩沖溢出,緩沖溢出是由于不正確地使用固定大小的數(shù)據(jù)結(jié)構(gòu)而造成的;
避免的方法:
使用更安全的不允許破壞程序的數(shù)據(jù)結(jié)構(gòu)——使用類似C++的string類
對不安全的數(shù)據(jù)結(jié)構(gòu)使用更安全的操作檢查所有的返回值
大多數(shù)難以察覺的錯誤都是因?yàn)槌绦騿T沒有檢查返回值而出現(xiàn)的重視所有稀有資源,審慎地管理它們的獲取和釋放
顯式地終止那些不再使用或不會被自動清除的對象的引用
不要循環(huán)引用(A引用B,B引用A)在聲明時對變量初始化
盡可能推遲變量的聲明
使變量聲明的位置和使用它的位置盡量接近,從而防止干擾其他代碼
不要在多個地方重用同一個臨時變量使用標(biāo)準(zhǔn)化語言工具,寫標(biāo)準(zhǔn)化語言
使用好的診斷信息日志工具
審慎地使用強(qiáng)制轉(zhuǎn)換
數(shù)據(jù)的強(qiáng)制轉(zhuǎn)換會影響代碼的可移植性細(xì)則
提供默認(rèn)行為:如同switch語句都帶default一樣,寫一個不帶else的if語句應(yīng)當(dāng)深思;
遵從語言習(xí)慣:
檢查數(shù)值上下限:防止數(shù)值型變量上溢和下溢;確保每次運(yùn)算可靠穩(wěn)定(被除量不能為0)。
正確的設(shè)置常量:盡可能把可以設(shè)置為常量的都設(shè)置為常量。約束
前置條件:輸入一段代碼前必須為真的條件,一般對參數(shù)作限定;
后置條件:編寫一段代碼后必須為真的條件,一般對結(jié)果作判斷;
不變條件:每當(dāng)程序執(zhí)行到達(dá)特定點(diǎn)(循環(huán)中、方法調(diào)用等)時為真的條件,防止邏輯錯誤;
斷言:任何關(guān)于程序在給定位置狀態(tài)的陳述;約束的內(nèi)容
檢查所有的數(shù)組訪問是否都在邊界內(nèi)
在廢棄指針之前斷言指針是非零的
確保函數(shù)參數(shù)有效
在函數(shù)結(jié)果返回之前對其進(jìn)行充分檢查移除約束
通常在程序構(gòu)建的開發(fā)和調(diào)試階段,才需要約束檢驗(yàn);確保程序邏輯正確后,理論上可以移除。
C/C++標(biāo)準(zhǔn)庫提供了公共機(jī)制——斷言;指定為NDEBUG編譯,可移除斷言
Java通過JVM啟動或禁用斷言機(jī)制
.NET在框架Debug中提供斷言機(jī)制
內(nèi)容相關(guān):
可重入代碼:允許被多個進(jìn)程同時訪問和使用的一段代碼,而且無論哪個進(jìn)程調(diào)用它,所得到的結(jié)果都是一樣。為了防止某一個進(jìn)程的修改而導(dǎo)致不同進(jìn)程的結(jié)果不同,可重入代碼中一般采用局部變量不使用全局變量或靜態(tài)變量。
線程安全:如果你的代碼所在的進(jìn)程中有多個線程在同時運(yùn)行,而這些線程可能會同時運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,表示你的代碼是線程安全的。
靜態(tài)分析:程序運(yùn)行前,執(zhí)行代碼檢查