靜態(tài)代碼檢查工具PC-Lint(一)
摘要:C/C++語(yǔ)言的語(yǔ)法擁有其它語(yǔ)言所沒(méi)有的靈活性,這種靈活性帶來(lái)了代碼效率的提升,但相應(yīng)增加了代碼中存在隱患的可能性。靜態(tài)代碼檢查工具PC-Lint則偏重于代碼的邏輯分析,它能夠發(fā)現(xiàn)代碼中潛在的錯(cuò)誤,比如數(shù)組訪(fǎng)問(wèn)越界、內(nèi)存泄漏、使用未初始化變量等。本文將介紹如何安裝和配置PC-Lint代碼檢查工具以及如何將PC-Lint與常見(jiàn)的代碼編輯軟件集成。
關(guān)鍵詞:代碼檢查 PC-Lint 規(guī)則 選項(xiàng)
目 錄 摘 要 1 引 言 2 PC-Lint介紹 3 PC-Lint的代碼檢查功能 3.1 強(qiáng)類(lèi)型檢查 3.2 變量值跟蹤 3.3 賦值順序檢查 3.4 弱定義檢查 3.5 格式檢查 3.6 縮進(jìn)檢查 3.7 const變量檢查 3.8 volatile變量檢查 4 PC-Lint軟件使用方法 4.1 安裝與配置 4.2 PC-Lint與常用開(kāi)發(fā)工具的集成(Visual C++,Source Insight,UEdit) 5 總結(jié) 參考文獻(xiàn) 附錄一 PC-Lint 重要文件說(shuō)明 附錄二 錯(cuò)誤信息禁止選項(xiàng)說(shuō)明 附錄三 PC-Lint檢測(cè)中的常見(jiàn)錯(cuò)誤
一 引言
C/C++語(yǔ)言的語(yǔ)法擁有其它語(yǔ)言所沒(méi)有的靈活性,這種靈活性帶來(lái)了代碼效率的提升,但相應(yīng)也使得代碼編寫(xiě)具有很大的隨意性,另外C/C++編譯器不進(jìn)行強(qiáng)制類(lèi)型檢查,也不做任何邊界檢查,這就增加了代碼中存在隱患的可能性。如果能夠在代碼提交測(cè)試之前發(fā)現(xiàn)這些潛在的錯(cuò)誤,就能夠極大地減輕測(cè)試人員的壓力,減少軟件項(xiàng)目的除錯(cuò)成本,可是傳統(tǒng)的C/C++編譯器對(duì)此已經(jīng)無(wú)能為力,這個(gè)任務(wù)只能由專(zhuān)用的代碼檢查工具完成。目前有很多C/C++靜態(tài)代碼檢查工具,其中Logiscope RuleChecker和PC-Lint是目前應(yīng)用比較廣泛的兩個(gè)工具。這兩個(gè)檢查工具各有特色,Logiscope RuleChecker傾向于代碼編碼規(guī)范的檢查,比如代碼縮進(jìn)格式、case語(yǔ)句書(shū)寫(xiě)規(guī)范、函數(shù)聲明和布爾表達(dá)式的編寫(xiě)規(guī)則等,而PC-Lint則偏重于代碼的邏輯分析,它能夠發(fā)現(xiàn)代碼中潛在的錯(cuò)誤,比如數(shù)組訪(fǎng)問(wèn)越界、內(nèi)存泄漏、使用未初始化變量等。本文將介紹如何安裝和配置PC-Lint代碼檢查工具以及將PC-Lint與常見(jiàn)的代碼編輯軟件,如Visual C++,Source Insight集成的方法,同時(shí)還將簡(jiǎn)要介紹一些PC-Lint常用的代碼檢查選項(xiàng)。
二 PC-Lint介紹
PC-Lint是GIMPEL SOFTWARE公司開(kāi)發(fā)的C/C++軟件代碼靜態(tài)分析工具,它的全稱(chēng)是PC-Lint/FlexeLint for C/C++,PC-Lint能夠在Windows、MS-DOS和OS/2平臺(tái)上使用,以二進(jìn)制可執(zhí)行文件的形式發(fā)布,而FlexeLint 運(yùn)行于其它平臺(tái),以源代碼的形式發(fā)布。PC-lint在全球擁有廣泛的客戶(hù)群,許多大型的軟件開(kāi)發(fā)組織都把PC-Lint檢查作為代碼走查的第一道工序。 PC-Lint不僅能夠?qū)Τ绦蜻M(jìn)行全局分析,識(shí)別沒(méi)有被適當(dāng)檢驗(yàn)的數(shù)組下標(biāo),報(bào)告未被初始化的變量,警告使用空指針以及冗余的代碼,還能夠有效地幫你提出許多程序在空間利用、運(yùn)行效率上的改進(jìn)點(diǎn)。 通過(guò)下面的例子就可以看出PC-Lint工具的強(qiáng)大功能: 1: 2:char *report( int m, int n, char *p ) 3:{ 4: int result; 5: char *temp; 6: long nm; 7: int i, k, kk; 8: char name[11] = "Joe Jakeson"; 9: 10: nm = n * m; 11: temp = p == "" ? "null" : p; 12: for( i = 0; i<m; I++ ) { 14: k++; 15: kk = i; 16: } 17: 18: if( k== 1 ) result = nm; 19: else if( kk > 0 ) result = 1; 20: else if( kk < 0 ) result = -1; 21: 22: if( m == result ) return( temp ); 23: else return( name ); 24:}
這是一段C代碼,可以通過(guò)大多數(shù)常見(jiàn)的C語(yǔ)言編譯器的檢查,但是PC-Lint能夠發(fā)現(xiàn)其中的錯(cuò)誤和潛在的問(wèn)題:第8行向name數(shù)組賦值時(shí)丟掉了結(jié)尾的 nul字符,第10行的乘法精度會(huì)失準(zhǔn),即使考慮到long比int的字長(zhǎng)更長(zhǎng),由于符號(hào)位的原因仍然會(huì)造成精度失準(zhǔn),第11行的比較有問(wèn)題,第14行的變量k沒(méi)有初始化,第15行的kk可能沒(méi)有被初始化,第22行的result也有可能沒(méi)有被初始化,第23行返回的是一個(gè)局部對(duì)象的地址。 隨著C++語(yǔ)言的出現(xiàn),C/C++編譯器有了更嚴(yán)格的語(yǔ)法檢查,但是仍然不能避免出現(xiàn)有BUG的程序。C++的類(lèi)型檢查依然不如Pascal那么嚴(yán)格。對(duì)于一個(gè)小程序,多數(shù)程序員都能夠及時(shí)發(fā)現(xiàn)上面出現(xiàn)的錯(cuò)誤,但是從一個(gè)擁有成千上萬(wàn)行代碼的大型軟件中找出這些瑕疵將是一項(xiàng)煩瑣的工作,而且沒(méi)有人可以保證能找出所有的這類(lèi)問(wèn)題。如果使用PC-Lint,只需通過(guò)一次簡(jiǎn)單的編譯就可以檢查出這些錯(cuò)誤,這將節(jié)省了大量的開(kāi)發(fā)時(shí)間。從某種意義上說(shuō)。PC- Lint是一種更加嚴(yán)格的編譯器,它除了可以檢查出一般的語(yǔ)法錯(cuò)誤外,還可以檢查出那些雖然符合語(yǔ)法要求,但很可能是潛在的、不易發(fā)現(xiàn)的錯(cuò)誤。
三 PC-Lint的代碼檢查功能
PC-Lint能夠檢查出很多語(yǔ)法錯(cuò)誤和語(yǔ)法上正確的邏輯錯(cuò)誤,PC-Lint為大部分錯(cuò)誤消息都分配了一個(gè)錯(cuò)誤號(hào),編號(hào)小于1000的錯(cuò)誤號(hào)是分配給C 語(yǔ)言的,編號(hào)大于1000的錯(cuò)誤號(hào)則用來(lái)說(shuō)明C++的錯(cuò)誤消息。表 1 列出了PC-Lint告警消息的詳細(xì)分類(lèi):
表 1 列出了PC-Lint告警消息分類(lèi)
錯(cuò)誤說(shuō)明 |
C |
C++ |
告警級(jí)別 |
語(yǔ)法錯(cuò)誤 |
1-199 |
1001-1199 |
1 |
內(nèi)部錯(cuò)誤 |
200-299 |
|
0 |
致命錯(cuò)誤 |
300-399 |
|
0 |
告警 |
400-699 |
1400-1699 |
2 |
消息 |
700-800 |
1700-1899 |
3 |
可選信息 |
900-999 |
1900-1999 |
4 |
以C 語(yǔ)言為例,其中的編號(hào)1-199指的是一般編譯器也會(huì)產(chǎn)生的語(yǔ)法錯(cuò)誤;編號(hào)200-299是PC-Lint程序內(nèi)部的錯(cuò)誤,這類(lèi)錯(cuò)誤不會(huì)出現(xiàn)在代碼中的;編號(hào)300-399指的是由于內(nèi)存限制等導(dǎo)致的系統(tǒng)致命錯(cuò)誤。編號(hào)400-999中出現(xiàn)的提示信息,是根據(jù)隱藏代碼問(wèn)題的可能性進(jìn)行分類(lèi)的:其中編號(hào) 400-699指的是被檢查代碼中很可能存在問(wèn)題而產(chǎn)生的告警信息;編號(hào)700-899中出現(xiàn)的信息,產(chǎn)生錯(cuò)誤的可能性相比告警信息來(lái)說(shuō)級(jí)別要低,但仍然可能是因?yàn)榇a問(wèn)題導(dǎo)致的問(wèn)題。編號(hào)900-999是可選信息,他們不會(huì)被默認(rèn)檢查,除非你在選項(xiàng)中指定檢查他們。 PC-Lint/FelexLint提供了和許多編譯器類(lèi)似的告警級(jí)別設(shè)置選項(xiàng)-wLevel,它的告警級(jí)別分為以下幾個(gè)級(jí)別,缺省告警級(jí)別為3級(jí): -w0 不產(chǎn)生信息(除了遇到致命的錯(cuò)誤) -w1 只生成錯(cuò)誤信息 -- 沒(méi)有告警信息和其它提示信息 -w2 只有錯(cuò)誤和告警信息 -w3 生成錯(cuò)誤、告警和其它提示信息(這是默認(rèn)設(shè)置) -w4 生成所有信息 PC-Lint/FelexLint還提供了用于處理函數(shù)庫(kù)的頭文件的告警級(jí)別設(shè)置選項(xiàng)-wlib(Level),這個(gè)選項(xiàng)不會(huì)影響處理C/C++源代碼模塊的告警級(jí)別。它有和-wLevel相同的告警級(jí)別,缺省告警級(jí)別為3級(jí): -wlib(0) 不生成任何庫(kù)信息 -wlib(1) 只生成錯(cuò)誤信息(當(dāng)處理庫(kù)的源代碼時(shí)) -wlib(2) 生成錯(cuò)誤和告警信息 -wlib(3) 生成錯(cuò)誤、告警和其它信息(這是默認(rèn)設(shè)置) -wlib(4) 產(chǎn)生所有信息 PC-Lint的檢查分很多種類(lèi),有強(qiáng)類(lèi)型檢查、變量值跟蹤、語(yǔ)義信息、賦值順序檢查、弱定義檢查、格式檢查、縮進(jìn)檢查、const變量檢查和 volatile變量檢查等等。對(duì)每一種檢查類(lèi)型,PC-Lint都有很多詳細(xì)的選項(xiàng),用以控制PC-Lint的檢查效果。PC-Lint的選項(xiàng)有300 多種,這些選項(xiàng)可以放在注釋中(以注釋的形式插入代碼中),例如: /*lint option1 option2 ... optional commentary */ 選項(xiàng)可以有多行 //lint option1 option2 ... optional commentary 選項(xiàng)僅為一行(適用于C++) 選項(xiàng)間要以空格分開(kāi),lint命令一定要小寫(xiě),并且緊跟在/*或//后面,不能有空格。如果選項(xiàng)由類(lèi)似于操作符和操作數(shù)的部分組成,例如-esym (534, printf, scanf, operator new),其中最后一個(gè)選項(xiàng)是operator new,那么在operator和new中間只能有一個(gè)空格。PC-Lint的選項(xiàng)還可以放在宏定義中,當(dāng)宏被展開(kāi)時(shí)選項(xiàng)才生效。例如: #define DIVZERO(x) /*lint -save -e54 */ ((x) /0) /*lint -restore */ 允許除數(shù)為0而不告警 下面將分別介紹PC-Lint常用的,也是比較重要的代碼檢查類(lèi)型,并舉例介紹了各個(gè)檢查類(lèi)型下可能出現(xiàn)的告警信息以及常用選項(xiàng)的用法:
3.1 強(qiáng)類(lèi)型檢查
強(qiáng)類(lèi)型檢查選項(xiàng)“-strong”和它的輔助(補(bǔ)充)選項(xiàng)“-index”可以對(duì)typedef定義的數(shù)據(jù)類(lèi)型進(jìn)行強(qiáng)類(lèi)型檢查,以保證只有相同類(lèi)型之間的變量才能互相賦值,強(qiáng)類(lèi)型檢查選項(xiàng)strong的用法是: -strong( flags[, name] ... ) strong選項(xiàng)必須在typedef定義類(lèi)型之前打開(kāi),否則PC-Lint就不能識(shí)別typedef定義的數(shù)據(jù)類(lèi)型,類(lèi)型檢查就會(huì)失效。flags參數(shù)可以是A、J、X、B、b、l和f,相應(yīng)的解釋和弱化字符在表 2 中列出:
表 2 強(qiáng)類(lèi)型檢查strong選項(xiàng)和參數(shù)表
A |
對(duì)強(qiáng)類(lèi)型變量賦值時(shí)進(jìn)行類(lèi)型檢查,這些賦值語(yǔ)句包括:直接賦值、返回值、參數(shù)傳遞、初始化 。 A參數(shù)后面可以跟以下字符,用來(lái)弱化A的檢查強(qiáng)度: i 忽略初始化 r 忽略Return語(yǔ)句 p 忽略參數(shù)傳遞 a 忽略賦值操作 c 忽略將常量賦值(包括整數(shù)常量、常量字符串等)給強(qiáng)類(lèi)型的情況 z 忽略Zero賦值,Zero定義為任何非強(qiáng)制轉(zhuǎn)換為強(qiáng)類(lèi)型的0常量。例如:0L和(int)0都是Zero, 但是(HANDLE)0當(dāng)HANDLE是一個(gè)強(qiáng)類(lèi)型的時(shí)候就不是Zero。(HANDLE *)0也不是例如使用-strong(Ai,BITS)設(shè)置,PC-Lint將會(huì)對(duì)從非BITS類(lèi)型數(shù)據(jù)向BITS類(lèi)型數(shù)據(jù)賦值的代碼發(fā)出告警,但是忽略變量初始化時(shí)的此類(lèi)賦值。 |
X |
當(dāng)把強(qiáng)類(lèi)型的變量賦指給其他變量的時(shí)候進(jìn)行類(lèi)型檢查。弱化參數(shù)i, r, p, a, c, z同樣適用于X并起相同的作用。 |
J |
選項(xiàng)是當(dāng)強(qiáng)類(lèi)型與其它類(lèi)型進(jìn)行如下的二進(jìn)制操作時(shí)進(jìn)行檢查,下面是J的參數(shù): e 忽略==、!=和?:操作符 r 忽略>、>=、<和<= o 忽略+、-、*、/、%、|、&和^ c 忽略該強(qiáng)類(lèi)型與常量進(jìn)行以上操作時(shí)的檢查 z 忽略該強(qiáng)類(lèi)型與Zero進(jìn)行以上操作時(shí)的檢查
使用忽略意味著不會(huì)產(chǎn)生告警信息。舉個(gè)例子,如果Meters是個(gè)強(qiáng)類(lèi)型,那么它只在判斷相等和其他關(guān)系操作時(shí)才會(huì)被正確地檢查,其它情況則不檢查,在這個(gè)例子中使用J選項(xiàng)是正確的。
|
B |
B選項(xiàng)有兩個(gè)效果: 1. 出于強(qiáng)類(lèi)型檢查的目的,假設(shè)所有的Boolean操作返回一個(gè)和Type兼容的類(lèi)型,所謂Boolean操作就是那些指示結(jié)果為true或false的操作,包括前面提到的四種關(guān)系運(yùn)算符和兩種等于判斷符,取反操作符!,二元操作符&&和||。 2. 在所有需要判斷Bolean值的地方,如if語(yǔ)句和while語(yǔ)句,都要檢查結(jié)果是否符合這個(gè)強(qiáng)類(lèi)型,否則告警。 例如if(a)...當(dāng)a為int時(shí),將產(chǎn)生告警,因?yàn)閕nt與Bolean類(lèi)不兼容,所以必須改為if(a != 0)。 |
b |
僅僅假定每一個(gè)Bolean類(lèi)操作符都將返回一個(gè)與Type類(lèi)型兼容的返回值。與B選項(xiàng)相比,b選項(xiàng)的限制比較寬松。 |
l |
庫(kù)標(biāo)志,當(dāng)強(qiáng)類(lèi)型的值作為參數(shù)傳遞給庫(kù)函數(shù)等情況下,不產(chǎn)生告警。 |
f |
與B或b連用,表示抑止對(duì)1bit長(zhǎng)度的位域是Boolean類(lèi)型的假定,如果不選該項(xiàng)表示1bit長(zhǎng)度的位域被缺省假定為Boolean類(lèi)型。 |
這些選項(xiàng)字符的順序?qū)δ軟](méi)有影響。但是A和J選項(xiàng)的弱化字符必須緊跟在它們之后。B選項(xiàng)和b選項(xiàng)不能同時(shí)使用,f選項(xiàng)必須搭配B選項(xiàng)或b選項(xiàng)使用,如果不指定這些選項(xiàng),-strong的作用就是僅僅聲明type為強(qiáng)類(lèi)型而不作任何檢查。下面用一段代碼演示-strong選項(xiàng)的用法:
//lint -strong(Ab,Bool) <選項(xiàng)是以注釋的形式插入代碼中> typedef int Bool; Bool gt(int a, b) { if(a) return a > b; // OK else return 0; // Warning } 例子代碼中Bool被聲明成強(qiáng)類(lèi)型,如果沒(méi)有指定b選項(xiàng),第一個(gè)return語(yǔ)句中的比較操作就會(huì)被認(rèn)為與函數(shù)類(lèi)型不匹配。第二個(gè)return語(yǔ)句導(dǎo)致告警是因?yàn)?不是各Bool類(lèi)型,如果添加c選項(xiàng),例如-strong(Acb,Bool),這個(gè)告警就會(huì)被抑制。再看一個(gè)例子: /*lint -strong( AJXl, STRING ) */ typedef char *STRING; STRING s; ... s = malloc(20); strcpy( s, "abc" );
由于malloc和strcpy是庫(kù)函數(shù),將malloc的返回值賦給強(qiáng)類(lèi)型變量s或?qū)?qiáng)類(lèi)型變量s傳遞給strcpy時(shí)會(huì)產(chǎn)生強(qiáng)類(lèi)型沖突,不過(guò)l選項(xiàng)抑制了這個(gè)告警。 強(qiáng)類(lèi)型也可用于位域,出于強(qiáng)類(lèi)型檢查的目的,先假定位域中最長(zhǎng)的一個(gè)字段是優(yōu)勢(shì)Boolean類(lèi)型,如果沒(méi)有優(yōu)勢(shì)Boolean或位域中沒(méi)有哪個(gè)字段比其它字段長(zhǎng),這個(gè)類(lèi)型從位域被切開(kāi)的位置開(kāi)始成為“散”類(lèi)型,例如: //lint -strong( AJXb, Bool ) //lint -strong( AJX, BitField ) typedef int Bool; typedef unsigned BitField; struct foo { unsigned a:1, b:2; BitField c:1, d:2, e:3; } x; void f() { x.a = (Bool) 1; // OK x.b = (Bool) 0; // strong type violation x.a = 0; // strong type violation x.b = 2; // OK x.c = x.a; // OK 118 x.e = 1; // strong type violation x.e = x.d; // OK }
上面例子中,成員a和c是強(qiáng)類(lèi)型Bool,成員d和e是BitField類(lèi)型,b不是強(qiáng)類(lèi)型。為了避免將只有一位的位域假設(shè)成Boolean類(lèi)型,需要在聲明Boolean的-strong中使用f選項(xiàng),上面的例子就應(yīng)該改成這樣:-strong(AJXbf,Bool)。
另一個(gè)強(qiáng)類(lèi)型檢查選項(xiàng)是index,index的用法是: -index( flags, ixtype, sitype [, sitype] ... ) 這個(gè)選項(xiàng)是對(duì)strong選項(xiàng)的補(bǔ)充,它可以和strong選項(xiàng)一起使用。這個(gè)選項(xiàng)指定ixtype是一個(gè)排除索引類(lèi)型,它可以和Strongly Indexed類(lèi)型sitype的數(shù)組(或指針)一起使用,ixtype和sitype被假設(shè)是使用typedef聲明的類(lèi)型名稱(chēng)。flags可以是c或 d,c允許將ixtype和常量作為索引使用,而d允許在不使用ixtype的情況下指定數(shù)組的長(zhǎng)度(Dimensions)。下面是一個(gè)使用index 的例子: //lint -strong( AzJX, Count, Temperature ) //lint -index( d, Count, Temperature ) // Only Count can index a Temperature typedef float Temperature; typedef int Count; Temperature t[100]; // OK because of d flag Temperature *pt = t; // pointers are also checked // ... within a function Count i; t[0] = t[1]; // Warnings, no c flag for( i = 0; i < 100; i++ ) t[i] = 0.0; // OK, i is a Count 119 pt[1] = 2.0; // Warning i = pt - t; // OK, pt-t is a Count
上面的例子中,Temperature是被強(qiáng)索引類(lèi)型,Count是強(qiáng)索引類(lèi)型。如果沒(méi)有使用d選項(xiàng),數(shù)組的長(zhǎng)度將被映射成固有的類(lèi)型: Temperature t[ (Count) 100 ]; 但是,這是個(gè)小麻煩,像下面那樣將數(shù)組長(zhǎng)度定義成常量更好一些: #define MAX_T (Count) 100 Temperature t[MAX_T];
這樣做還有一個(gè)好處就是同樣的MAX_T還可以用在for語(yǔ)句中,用于限制for語(yǔ)句的范圍。需要注意的是,指向強(qiáng)被索引類(lèi)型的指針(例如上面的pt)如果用在[]符號(hào)(數(shù)組符號(hào))中也會(huì)被檢查類(lèi)型。其實(shí),無(wú)論何時(shí),只要將一個(gè)值加到一個(gè)指向強(qiáng)被索引類(lèi)型的指針時(shí),這個(gè)值就會(huì)被檢查以確認(rèn)它是一個(gè)強(qiáng)索引類(lèi)型。此外,強(qiáng)被索引指針如果減去一個(gè)值,其結(jié)果被認(rèn)為是平常的強(qiáng)索引,所以下面的例子就不會(huì)產(chǎn)生告警: i = pt - t;
3.2 變量值跟蹤
3.2.1 變量值初始化跟蹤
早期的變量值跟蹤技術(shù)主要是對(duì)變量值的初始化進(jìn)行跟蹤,和變量初始化相關(guān)的LINT消息主要是644, 645 ("變量可能沒(méi)有初始化"), 771, 772 ("不可靠的初始化"), 530 ("未初始化的"), and 1401 - 1403 ("成員 ... 未初始化")。以下面的代碼為例: if( a ) b = 6; else c = b; // 530 message a = c; // 645 message
假設(shè)b和c在之前都沒(méi)有初始化,PC-Lint就會(huì)報(bào)告b沒(méi)有初始化(在給c賦值的時(shí)候)和c可能沒(méi)有被初始化(在給a賦值的時(shí)候)的消息。而while和for循環(huán)語(yǔ)句和上面的if語(yǔ)句稍微有所不同,比較下面的代碼: while ( n-- ) { b = 6; ... } c = b; //772 message
假設(shè)b在使用之前沒(méi)有被初始化,這里會(huì)報(bào)告b可能沒(méi)有初始化的消息(當(dāng)給c賦值時(shí))。之所以會(huì)有這樣的區(qū)別,是因?yàn)槌绦蛟O(shè)計(jì)者可能知道這樣的循環(huán)體總是會(huì)被至少執(zhí)行一次。相反,前面的if語(yǔ)句,對(duì)于程序設(shè)計(jì)者來(lái)說(shuō)比較難以確定if語(yǔ)句是否總會(huì)被執(zhí)行,因?yàn)槿绻沁@樣的話(huà),這樣的if語(yǔ)句就是多余的,應(yīng)該被去掉。While語(yǔ)句和if比較相似,看下面的例子: switch ( k ) { case 1: b = 2; break; case 2: b = 3; /* Fall Through */ case 3: a = 4; break; default: error(); } c = b; //645 message
盡管b在兩個(gè)不同的地方被賦值,但是仍然存在b沒(méi)有被初始化的可能。因此,當(dāng)b賦值給c的時(shí)候,就會(huì)產(chǎn)生可能沒(méi)有初始化的消息。為了解決這個(gè)問(wèn)題,你可以在 switch語(yǔ)句之前給b賦一個(gè)默認(rèn)值。這樣PC-Lint就不會(huì)產(chǎn)生告警消息,但是我們也失去了讓PC-Lint檢查后續(xù)的代碼修改引起的變量初始化問(wèn)題的機(jī)會(huì)。更好的方法是修改沒(méi)有給b賦值的case語(yǔ)句。 如果error()語(yǔ)句代表那些“不可能發(fā)生”的事情發(fā)生了,那么我們可以讓PC-Lint知道這一段其實(shí)是不可能執(zhí)行的,下面的代碼表明了這一點(diǎn): switch ( k ) { case 1: b = 2; break; case 2: case 3: b = 3; a = 4; break; default: error(); /*lint -unreachable */ } c = b; 注意:這里的-unreachable應(yīng)該放在error()后面,break的前面。另外一個(gè)產(chǎn)生”沒(méi)有初始化”告警的方式是傳遞一個(gè)指針給free(或者采用相似的方法)。比如: if( n ) free( p ); ... p->value = 3; 在訪(fǎng)問(wèn)p的時(shí)候會(huì)產(chǎn)生p可能沒(méi)有被初始化的消息。對(duì)于goto語(yǔ)句,前向的goto可能產(chǎn)生沒(méi)有初始化消息,而向后的goto 會(huì)被忽略掉這種檢查。 if ( a ) goto label; b = 0; label: c = b; 當(dāng)在一個(gè)大的項(xiàng)目中使用未初始化變量檢查時(shí),可能會(huì)產(chǎn)生一些錯(cuò)誤的報(bào)告。這種報(bào)告的產(chǎn)生,很大一部分來(lái)自于不好的程序設(shè)計(jì)風(fēng)格,或者包括下面的結(jié)構(gòu): if( x ) initialize y ... if( x ) use y 當(dāng)出現(xiàn)這種情況時(shí),可以采用給y賦初始值的方式,或者利用選項(xiàng)-esym(644,y)關(guān)掉變量y上面的初始化檢查。
3.2.2 變量值跟蹤
變量值跟蹤技術(shù)從賦值語(yǔ)句、初始化和條件語(yǔ)句中收集信息,而函數(shù)的參數(shù)被默認(rèn)為在正確的范圍內(nèi),只有在從函數(shù)中可以收集到的信息與此不符的情況下才產(chǎn)生告警。與變量值跟蹤相關(guān)的消息有: (1) 訪(fǎng)問(wèn)地址越界消息(消息415,661,796) (2) 被0除消息(54,414,795) (3) NULL指針的錯(cuò)誤使用(413,613,794) (4) 非法指針的創(chuàng)建錯(cuò)誤(416,662,797) (5) 冗余的布爾值測(cè)試(774)
看下面的例子: int a[10]; int f() { int k; k = 10; return a[k]; // Warning 415 } 這個(gè)語(yǔ)句會(huì)產(chǎn)生警告415(通過(guò) '[' 訪(fǎng)問(wèn)越界的指針),因?yàn)镻C-Lint保存了賦給k的值,然后在使用k的時(shí)候進(jìn)行了判斷。如果我們把上面的例子稍加修改: int a[10]; int f( int n ) { int k; if ( n ) k = 10; else k = 0; return a[k]; // Warning 661 } 這樣就會(huì)產(chǎn)生告警 661 (可能訪(fǎng)問(wèn)越界指針)。 使用“可能”是因?yàn)椴皇撬械穆窂蕉紩?huì)把10賦值給k。PC-Lint不僅收集賦值語(yǔ)句和初始化,還從條件語(yǔ)句中收集值的信息。比如下面的例子: int a[10]; int f( int k, int n ) { if ( k >= 10 ) a[0] = n; return a[k]; // Warning 661 -- k could be 10 } 這里仍然產(chǎn)生661告警,因?yàn)镻C-Lint檢測(cè)到,在使用k的時(shí)候,k的值>=10。另外,對(duì)于函數(shù)來(lái)說(shuō),它總是假設(shè)K是正確的,程序使用者知道他們要做些什么,所以下面的語(yǔ)句不會(huì)產(chǎn)生告警: int a[10]; int f( int k, int n ) { return a[k+n]; } // no warning 和檢查變量沒(méi)有初始化一樣,還可以檢查變量的值是否正確。比如,如果下面例子中的循環(huán)一次都沒(méi)有運(yùn)行,k可能會(huì)超出范圍。這時(shí)候會(huì)產(chǎn)生消息796 (可預(yù)見(jiàn)的地址訪(fǎng)問(wèn)越界). int a[10]; int f(int n, int k) { int m = 2; if( k >= 10 ) m++; // Hmm -- So k could be 10, eh? while( n-- ) { m++; k = 0; } return a[k]; // Info 796 - - k could still be 10 } 下面的例子演示了可能使用NULL指針的問(wèn)題: int *f( int *p ) { if ( p ) printf( "\n" ); // So -- p could be NULL printf( "%d", *p ); // Warning return p + 2; // Warning } 這里會(huì)產(chǎn)生兩個(gè)告警,因?yàn)榭赡苁褂昧薔ULL指針,很明顯,這兩個(gè)語(yǔ)句應(yīng)該在if語(yǔ)句的范圍內(nèi)。為了使你的程序更加健壯,你可能需要打開(kāi)Pointer- parameter-may-be-NULL這個(gè)開(kāi)關(guān)(+fpn)。這個(gè)選項(xiàng)假設(shè)所有傳遞到函數(shù)中的指針都有可能是NULL的。數(shù)組邊界值在高位被檢測(cè),也就是說(shuō) int a[10]; ... a[10] = 0; 被檢測(cè)了,而a[-1]卻檢測(cè)不到。PC-Lint中有兩個(gè)消息是和指針的越界檢查有關(guān)的,一個(gè)是越界指針的創(chuàng)建,另外一個(gè)是越界指針的訪(fǎng)問(wèn),也就是通過(guò)越界指針獲取值。在ANSI C([1]3.3.6)中,允許創(chuàng)建指向超過(guò)數(shù)組末尾一個(gè)單元的指針,比如: int a[10]; f( a + 10 ); // OK f( a + 11 ); // error 但是上面創(chuàng)建的兩個(gè)指針,都是不能訪(fǎng)問(wèn)的,比如: int a[10], *p, *q; p = a + 10; // OK *p = 0; // Warning (access error) p[-1] = 0; // No Warning q = p + 1; // Warning (creation error) q[0] = 0; // Warning (access error) 布爾條件檢查不象指針檢查那么嚴(yán)格,但是它會(huì)對(duì)恒真的布爾條件產(chǎn)生告警,比如: if ( n > 0 ) n = 0; else if ( n <= 0 ) n = -1; // Info 774 上面的代碼會(huì)產(chǎn)生告警(774),因?yàn)榈诙€(gè)條件檢查是恒真的,可以忽略。這種冗余代碼不會(huì)導(dǎo)致問(wèn)題,但它的產(chǎn)生通常是因?yàn)檫壿嬪e(cuò)誤或一種錯(cuò)誤可能發(fā)生的征兆,需要詳細(xì)的檢查。
3.2.3 使用assert(斷言)進(jìn)行補(bǔ)救
在某些情況下,雖然根據(jù)代碼我們可以知道確切的值,但是PC-Lint卻無(wú)法獲取所有情況下變量的值的范圍,這時(shí)候會(huì)產(chǎn)生一些錯(cuò)誤的告警信息,我們可以使用assert語(yǔ)句增加變量取值范圍信息的方法,來(lái)抑制這些錯(cuò)誤的告警信息的產(chǎn)生。下面舉例來(lái)說(shuō)明: char buf[4]; char *p; strcpy( buf, "a" ); p = buf + strlen( buf ); // p is 'possibly' (buf+3) p++; // p is 'possibly' (buf+4) *p = 'a'; // Warning 661 - possible out-of-bounds reference PC-Lint無(wú)法知道在所有情況下變量的值是多少。在上面的例子中,產(chǎn)生告警的語(yǔ)句其實(shí)并不會(huì)帶來(lái)什么危害。我們可以直接使用 *p = 'a'; //lint !e661 來(lái)抑制告警。另外,我們還可以使用assert工具來(lái)修正這個(gè)問(wèn)題: #include <assert.h> ... char buf[4]; char *p; strcpy( buf, "a" ); p = buf + strlen( buf ); assert( p < buf + 3 ); // p is 'possibly' (buf+2) p++; // p is 'possibly' (buf+3) *p = 'a'; // no problem 由于assert在NDEBUG被定義時(shí)是一個(gè)空操作,所以要保證Lint進(jìn)行的時(shí)候這個(gè)宏沒(méi)有被定義。
為了使assert()和你的編譯器自帶的assert.h一起產(chǎn)生上面的效果,你需要在編譯選項(xiàng)文件中添加一個(gè)選項(xiàng)。例如,假設(shè)assert 是通過(guò)以下的編譯器宏定義實(shí)現(xiàn)的: #define assert(p) ((p) ? (void)0 : __A(...)) 考慮到__A()會(huì)彈出一個(gè)消息并且不會(huì)返回,所以這個(gè)需要添加的選項(xiàng)就是: -function( exit, __A ) 這個(gè)選項(xiàng)將exit函數(shù)的一些非返回特征傳遞給__A函數(shù)。做為選擇結(jié)果,編譯器可能將assert實(shí)現(xiàn)成一個(gè)函數(shù),例如: #define assert(k) _Assert(k,...) 為了讓PC-lint知道_Assert是一個(gè)assert函數(shù),你需要使用-function( __assert, _Assert )選項(xiàng)或-function( __assert(1), _Assert(1) )選項(xiàng)復(fù)制__assert()函數(shù)的語(yǔ)義 許多編譯器的編譯選項(xiàng)文件中已經(jīng)存在這些選項(xiàng)了,如果沒(méi)有的話(huà),你可以復(fù)制一個(gè)assert.h文件到PC-lint目錄下(這個(gè)目錄由于使用了-i選項(xiàng),文件搜索的順序優(yōu)先于編譯器的頭文件目錄)。
3.2.4 函數(shù)內(nèi)變量跟蹤
PC-Lint的函數(shù)值跟蹤功能會(huì)跟蹤那些將要傳遞給函數(shù)(作為函數(shù)參數(shù))變量值,當(dāng)發(fā)生函數(shù)調(diào)用時(shí),這些值被用來(lái)初始化函數(shù)參數(shù)。這種跟蹤功能被用來(lái)測(cè)定返回值,記錄額外的函數(shù)調(diào)用,當(dāng)然還可以用來(lái)偵測(cè)錯(cuò)誤。考察下面的例子代碼: t1.cpp: 1 int f(int); 2 int g() 3 { return f(0); } 4 int f( int n ) 5 { return 10 / n; } 在這個(gè)例子中,f()被調(diào)用的時(shí)候使用0作為參數(shù),這將導(dǎo)致原本沒(méi)有問(wèn)題的10/n語(yǔ)句產(chǎn)生被0除錯(cuò)誤,使用命令lin -u t1.cpp可以得到以下輸出: --- Module: t1.cpp During Specific Walk: File t1.cpp line 3: f(0) t1.cpp 5 Warning 414: Possible division by 0 [Reference:File t1.cpp: line 3] 你第一個(gè)注意到的事情是短語(yǔ)“During Specific Walk”,緊接著是函數(shù)調(diào)用發(fā)生的位置,函數(shù)名稱(chēng)以及參數(shù),再下來(lái)就是錯(cuò)誤信息。如果錯(cuò)誤信息中缺少了錯(cuò)誤再現(xiàn)時(shí)的錯(cuò)誤行和用來(lái)標(biāo)記錯(cuò)誤位置的指示信息,這是因?yàn)闄z查到錯(cuò)誤的時(shí)候代碼(被調(diào)用函數(shù)的代碼)已經(jīng)走過(guò)了。如果像下面一樣調(diào)換一下兩個(gè)函數(shù)的位置: t2.cpp: 1 int f( int n ) 2 { return 10 / n; } 3 int g() 4 { return f(0); } 這種情況下就不會(huì)出現(xiàn)被0除的告警,因?yàn)榇藭r(shí)f(0)在第四行,函數(shù)f()的代碼已經(jīng)過(guò)了,在這種情況下就需要引入multi-pass選項(xiàng)。如果在剛才的例子中使用lin -u -passes(2) t2.cpp命令,那么輸出就變成: --- Module: t2.cpp /// Start of Pass 2 /// --- Module: t2.cpp During Specific Walk: File t2.cpp line 4: f(0) t2.cpp 2 Warning 414: Possible division by 0 [Reference:File t2.cpp: line 4]
使用-passes(2)選項(xiàng)將會(huì)檢查代碼兩遍,一些操作系統(tǒng)不支持在命令行中使用-passes(2),對(duì)于這樣的系統(tǒng),可以使用-passes=2 或 -passes[2]代替。通過(guò)冗長(zhǎng)的信息可以看出來(lái),以pass 2開(kāi)始表示第一次檢查沒(méi)有產(chǎn)生告警信息。這一次得到的錯(cuò)誤信息和前一次不同,在某種情況下我們可以推斷出指定函數(shù)調(diào)用的返回值,至少可以得到一些返回值的屬性。以下面的模塊為例: t3.cpp: 1 int f( int n ) 2 { return n - 1; } 3 int g( int n ) 4 { return n / f(1); } 使用命令 lin -u -passes(2) t3.cpp,可以得到以下輸出信息: --- Module: t3.cpp /// Start of Pass 2 /// --- Module: t3.cpp
{ return n / f(1); } t3.cpp 4 Warning 414: Possible division by 0 [Reference:File t3.cpp: lines 2, 4]
第一遍檢查我們知道調(diào)用函數(shù)f()傳遞的參數(shù)是1,第二遍檢查先處理了函數(shù)f(),我們推斷出這個(gè)參數(shù)將導(dǎo)致返回結(jié)果是0,當(dāng)?shù)诙闄z查開(kāi)始處理函數(shù)g() 的時(shí)候,產(chǎn)生了被0除錯(cuò)誤。應(yīng)該注意到這個(gè)信息并不是在短語(yǔ)“During Specific Walk”之前出現(xiàn)的,這是因?yàn)殄e(cuò)誤是在對(duì)函數(shù)g()進(jìn)行正常的處理過(guò)程中檢測(cè)到的,此時(shí)并沒(méi)有使用為函數(shù)g()的參數(shù)指定的值。指定的函數(shù)調(diào)用能夠產(chǎn)生附加的函數(shù)調(diào)用,如果我們pass足夠多的檢測(cè)次數(shù),這個(gè)過(guò)程可能會(huì)重復(fù)發(fā)生,參考下面的代碼: t4.cpp: 1 int f(int); 2 int g( int n ) 3 { return f(2); } 4 int f( int n ) 5 { return n / f(n - 1); } 第五行的分母f(n-1)并不會(huì)引起懷疑,直到我們意識(shí)到f(2)調(diào)用將導(dǎo)致f(1)調(diào)用,最終會(huì)調(diào)用f(0),迫使最終的返回值是0。使用下面的命令行: lin -u -passes(3) t4.cpp, 輸出結(jié)果如下: --- Module: t4.cpp { return f(2); } t4.cpp 3 Info 715: Symbol 'n' (line 2) not referenced /// Start of Pass 2 /// --- Module: t4.cpp /// Start of Pass 3 /// --- Module: t4.cpp During Specific Walk: File t4.cpp line 3: f(2) File t4.cpp line 5: f(1) t4.cpp 5 Warning 414: Possible division by 0 [Reference:File t4.cpp: lines 3, 5] 到這里已經(jīng)處理了三遍才檢測(cè)到可能的被0除錯(cuò)誤,想了解為什么需要處理三遍可以看看這個(gè)選項(xiàng)-specific_wlimit(n)。需要注意的是,指定的調(diào)用序列,f(2),f(2),是作為告警信息的序言出現(xiàn)的。
3.3 賦值順序檢查
當(dāng)一個(gè)表達(dá)式的值依賴(lài)于賦值的順序的時(shí)候,會(huì)產(chǎn)生告警564。這是C/C++語(yǔ)言中非常普遍的一個(gè)問(wèn)題,但是很少有編譯器會(huì)分析這種情況。比如 n++ + n 這個(gè)語(yǔ)句是有歧義的,當(dāng)左邊的+操作先執(zhí)行的話(huà),它的值會(huì)比右邊的先執(zhí)行的值大一,更普遍的例子是這樣的: a[i] = i++; f( i++, n + i ); 第一個(gè)例子,看起來(lái)好像自加操作應(yīng)該在數(shù)組索引計(jì)算以后執(zhí)行,但是如果右邊的賦值操作是在左邊賦值操作之前執(zhí)行的話(huà),那么自加一操作就會(huì)在數(shù)組索引計(jì)算之前執(zhí)行。雖然,賦值操作看起來(lái)應(yīng)該指明一種操作順序,但實(shí)際上是沒(méi)有的。第二個(gè)例子是有歧義的,是因?yàn)楹瘮?shù)的參數(shù)值的計(jì)算順序也是沒(méi)有保證的。能保證賦值順序的操作符是布爾與(&&)或(||)和條件賦值(? :)以及逗號(hào)(,),因此: if( (n = f()) && n > 10 ) ... 這條語(yǔ)句是正確的,而: if( (n = f()) & n > 10 ) ... 將產(chǎn)生一條告警。
3.4 弱定義檢查
這里的弱定義包含是以下內(nèi)容:宏定義、typedef名字、聲明、結(jié)構(gòu)、聯(lián)合和枚舉類(lèi)型。因?yàn)檫@些東西可能在模塊中被過(guò)多定義且不被使用,PC-Lint 有很多消息用來(lái)檢查這些問(wèn)題。PC-Lint的消息749-769 和1749-1769都是保留用來(lái)作為弱定義提示的。 (1) 當(dāng)一個(gè)文件#include的頭文件中沒(méi)有任何引用被該文件使用,PC-Lint會(huì)發(fā)出766告警。 (2) 為了避免一個(gè)頭文件變得過(guò)于大而臃腫,防止其中存在冗余的聲明,當(dāng)一個(gè)頭文件中的對(duì)象聲明沒(méi)有被外部模塊引用到時(shí),PC-Lint會(huì)發(fā)出759告警。 (3) 當(dāng)變量或者函數(shù)只在模塊內(nèi)部使用的時(shí)候,PC-Lint會(huì)產(chǎn)生765告警,來(lái)提示該變量或者函數(shù)應(yīng)該被聲明為static。 如果你想用PC-Lint檢查以前沒(méi)有檢查過(guò)的代碼,你可能更想將這些告警信息關(guān)閉,當(dāng)然,如果你只想查看頭文件的異常,可以試試這個(gè)命令: lint -w1 +e749 +e?75? +e?76? ...
靜態(tài)代碼檢查工具PC-Lint(二)
3.5 格式檢查
PC-Lint會(huì)檢查printf和scanf(及其家族)中的格式?jīng)_突,例如: printf( "%+c", ... ) 將產(chǎn)生566告警,因?yàn)榧犹?hào)只在數(shù)字轉(zhuǎn)換時(shí)有用,有超過(guò)一百個(gè)這樣的組合會(huì)產(chǎn)生告警,編譯器通常不標(biāo)記這些矛盾,其他的告警還有對(duì)壞的格式的抱怨,它們是 557和567。我們遵循ANSI C建立的規(guī)則,可能更重要的是我們還對(duì)大小不正確的格式進(jìn)行標(biāo)記(包括告警558, 559, 560 和 561)。比如 %d 格式,允許使用int和unsigned int,但是不支持double和long(如果long比int長(zhǎng)),同樣,scanf需要參數(shù)指向的對(duì)象大小正確。如果只是參數(shù)的類(lèi)型(不是大小)與格式不一致,那將產(chǎn)生626和627告警。-printf 和 -scanf選項(xiàng)允許用戶(hù)指定與printf或scanf函數(shù)族類(lèi)似的函數(shù),-printf_code 和 -scanf_code也可以被用來(lái)描述非標(biāo)準(zhǔn)的 % 碼。
3.6 縮進(jìn)檢查
根據(jù)代碼中的縮進(jìn)問(wèn)題,PC-Lint也會(huì)產(chǎn)生相應(yīng)的告警,因?yàn)榭s進(jìn)的問(wèn)題有很大一部分是由于代碼結(jié)構(gòu)不良或者大括號(hào)的遺漏造成的。比如下面的例子: if( ... ) if( ... ) statement else statement 很明顯這里的else是和第一個(gè)if語(yǔ)句對(duì)應(yīng)的,而在這里編譯器則把它和第二個(gè)if對(duì)應(yīng)起來(lái)。PC-Lint會(huì)對(duì)這種情況產(chǎn)生告警。和這樣的縮進(jìn)檢查相關(guān)的告警主要有三個(gè)725(no positive indentation)、525(negatively indented from)、539(Did not expect positive indentation from Location)要進(jìn)行縮進(jìn)檢查,我們首先要設(shè)置文件中的tab鍵所對(duì)應(yīng)的空格數(shù),默認(rèn)的是占用8個(gè)空格,這個(gè)參數(shù)可以用-t#選項(xiàng)進(jìn)行修改。比如- t4表示tab鍵占用4個(gè)空格長(zhǎng)度。另外,縮進(jìn)檢查還和代碼的編碼格式策略相關(guān),需要進(jìn)行必要的調(diào)整。
3.7 const變量檢查
對(duì)于const變量的檢查,PC-Lint是完全支持的。使用const變量,對(duì)于提高代碼的質(zhì)量非常有好處,看一下下面的例子: char *strcpy( char *, const char * ); const char c = 'a'; const char *p = &c; void main() { char buf[100]; c = 'b'; *p = 'c'; strcpy( p, buf ); ... 這里的c和*P指向的內(nèi)容都是靜態(tài)變量,不可修改。上面的代碼明顯違反了這個(gè)規(guī)定,會(huì)產(chǎn)生Error(11),另外,把P作為第一個(gè)參數(shù)傳入strcpy 中,會(huì)產(chǎn)生告警605(Increase in pointer capability),而把buf作為第二個(gè)參數(shù)傳入strcpy函數(shù)中,會(huì)產(chǎn)生告警603(Symbol 'Symbol' (Location) not initialized),因?yàn)閎uf沒(méi)有初始化,而作為靜態(tài)變量的第二個(gè)參數(shù),是不能在strcpy函數(shù)中再被初始化的。
3.8 volatile變量檢查
對(duì)于volatile變量的檢查,在PC-Lint中有這樣的規(guī)定,如果一個(gè)表達(dá)式中同時(shí)使用了兩次相同的volatile變量,那么就會(huì)給出564告警,因?yàn)檫@時(shí)候會(huì)產(chǎn)生賦值順序的問(wèn)題。 volatile char *p; volatile char f(); n = (f() << 8) | f(); /* Warning 564 */ n = (*p << 8) | *p; /* Warning 564 */
四 PC-Lint軟件使用方法
4.1 安裝與配置
PC-lint軟件性?xún)r(jià)比高,易于學(xué)習(xí),容易推廣和固化到軟件開(kāi)發(fā)測(cè)試流程中去,所以在全世界得到了廣泛的應(yīng)用。PC-lint使用方法很簡(jiǎn)單,可以用命令行方式進(jìn)行,例如lint-nt –u std.lnt test1.c test2.c test3.c 也可以使用MAKEFILE的方式。此外,它還可以集成到很多開(kāi)發(fā)環(huán)境或常用的代碼編輯軟件中,比如集成到Source Insight/SLICKEDIT/MS VC6.0/KEIL C..等。PC-Lint還支持Scott Meyes的名著(Effective C++/More Effective C++)中說(shuō)描述的各種提高效率和防止錯(cuò)誤的方法。 PC-lint的安裝非常簡(jiǎn)單,以PC-lint 8.0為例,運(yùn)行安裝程序?qū)⑵溽尫诺街付ǖ陌惭b目錄即可,比如c:\pclint8。然后需要運(yùn)行PC-lint的配置工具config.exe生成選項(xiàng)和檢查配置文件,以剛才的安裝路徑為例,config.exe應(yīng)該位于:C:\pclint8\config.exe。配置文件是代碼檢查的依據(jù),PC- lint自帶了一個(gè)標(biāo)準(zhǔn)配置文件std.lnt,但是這個(gè)文件沒(méi)有目錄包含信息(頭文件目錄),通常對(duì)代碼檢查的時(shí)候都需要指定一些特殊的包含目錄,所以要在標(biāo)準(zhǔn)配置的基礎(chǔ)上生成針對(duì)某個(gè)項(xiàng)目代碼檢查的定制配置。下面就以Microsoft Visual C++ 6的開(kāi)發(fā)環(huán)境為例,介紹一下定制配置的過(guò)程。 運(yùn)行C:\pclint8\config.exe后出現(xiàn)一個(gè)歡迎界面,提示版權(quán)信息,如圖4.1所示:
 圖4.1 配置歡迎窗口
點(diǎn)擊“下一步”按鈕出現(xiàn)pc-lint.exe命令行使用說(shuō)明窗口(圖4.2所示):

圖4.2 pc-lint.exe命令行使用說(shuō)明窗口
點(diǎn)擊“下一步”按鈕繼續(xù),接著是選擇創(chuàng)建或修改已有配置文件STD.LNT的選項(xiàng):
 圖4.3 選擇如何使用配置文件STD.LNT
因?yàn)槲覀兪堑谝淮闻渲?,所以選擇上面一個(gè)選項(xiàng)“Create a new STD.LNT”,這樣做不會(huì)修改已有配置文件STD.LNT的內(nèi)容,而是創(chuàng)建一個(gè)新的STD_x.LNT文件,文件名中的x是從“a”到“z”26個(gè)英文字符中的任意一個(gè),一般是按順序排列,從“a”開(kāi)始。STD_x.LNT文件的內(nèi)容被初始化為STD.LNT內(nèi)容的拷貝。如圖4.3所示,使用默認(rèn)的 PC-Lint路徑,然后點(diǎn)擊“下一步”按鈕選擇編譯器:

圖4.4 選擇編譯器
接下來(lái)是選擇編譯器,在下拉框中選擇自己使用的編譯器。這里我們選擇“Microsoft Visual C++ 6.x (co-msc60.lnt)”。如果沒(méi)有自己使用的編譯器,可選擇通用編譯器“Generic Compilers”。這個(gè)選項(xiàng)會(huì)體現(xiàn)在co-xxx.lnt文件中,并存放在前面我們選擇的配置路徑(C:\PCLint8)下,在后面配置選項(xiàng)我們所選擇的***.LNT均會(huì)被存放到這個(gè)路徑下。點(diǎn)擊“下一步”按鈕選擇內(nèi)存模式:
 圖4.5 選擇內(nèi)存模式
可以根據(jù)自己程序區(qū)和數(shù)據(jù)區(qū)的實(shí)際大小選擇一個(gè)恰當(dāng)?shù)膬?nèi)存模型,內(nèi)存模型的選項(xiàng)會(huì)體現(xiàn)在STD.LNT文件或新創(chuàng)建的STD_x.LNT中。因?yàn)槲覀兊拈_(kāi)發(fā)環(huán)境是32位的Windows,所以選擇“32-bit Flat Model”,然后點(diǎn)擊“下一步”按鈕選擇所要的支持庫(kù)的配置信息:
 圖4.6 選擇軟件庫(kù)的配置信息
PC -Lint對(duì)現(xiàn)在常用的一些軟件庫(kù)都提供了定制的配置信息,選擇這些定制信息有助于開(kāi)發(fā)人員將錯(cuò)誤或信息的注意力集中在自己的代碼中,選擇的支持庫(kù)配置將被引入到STD.LNT文件或新創(chuàng)建的STD_x.LNT文件中。選擇常用的ATL、MFC、STL等配置,然后點(diǎn)擊“下一步”按鈕:
 圖4.7 選擇軟件名人的編程建議
這是一個(gè)比較有意思的選項(xiàng),就是讓你選擇是否支持為使用C/C++編程提出過(guò)重要建議的作者的一些關(guān)于編程方面的個(gè)人意見(jiàn)。如果選擇某作者的建議,那么他提出的編程建議方面的選項(xiàng)將被打開(kāi),作者建議的配置名為AU-xxx.LNT,建議全部選擇,然后點(diǎn)擊“下一步”按鈕:

圖4.8 選擇是否現(xiàn)在設(shè)置包含文件目錄
接下來(lái)是選擇用何種方式設(shè)置包含文件目錄,如果選擇使用-i方式協(xié)助設(shè)置包含文件選項(xiàng),下一步就會(huì)要求輸入一個(gè)或多個(gè)包含路徑。也可以跳過(guò)這一步,以后手工修改配置文件,-i選項(xiàng)體現(xiàn)在STD.LNT文件或新創(chuàng)建的STD_x.LNT文件中,每個(gè)目錄前以-i引導(dǎo),目錄間以空格分隔,如果目錄名中有長(zhǎng)文件名或包含空格,使用時(shí)要加上雙引號(hào),如-i“E:\Program Files\Microsoft Visual C++\VC98\Indlue”。這里我們選擇用-i方式協(xié)助我們來(lái)設(shè)置,然后點(diǎn)擊“下一步”按鈕:
 圖4.9 選擇是否現(xiàn)在設(shè)置包含文件目錄
這一步就是在下面的文本框里可手工輸入文件包含路徑,用分號(hào)“;”或用ctrl+Enter換行來(lái)分割多個(gè)包含路徑,或者可以點(diǎn)中Brows,在目錄樹(shù)中直接選擇。填完后點(diǎn)擊“下一步”按鈕:
 圖4.10 提示std_x.lnt已經(jīng)被創(chuàng)建
因?yàn)榈谌竭x擇了“Create a new STD.LNT”選項(xiàng),所以出現(xiàn)以下對(duì)話(huà)框,表示std_x.lnt,std.lnt在配置路徑下已被創(chuàng)建,這里的std_a.lnt實(shí)際上包含了 std.lnt的信息,除此之外還有我們選擇的包含路徑和庫(kù)配置信息。單擊“確定”按鈕繼續(xù):
 圖4.11 提示是否為其它編譯環(huán)境創(chuàng)建配置文件
選擇“確定”后,會(huì)接著提示是否為其它編譯環(huán)境創(chuàng)建配置文件,如果選擇“是”將從第四步開(kāi)始創(chuàng)建一個(gè)新的配置文件。這里我們選擇“否”:
 圖4.12 是否替換std.lnt文件
接下來(lái)會(huì)提示是否使用現(xiàn)在生成的std_x.lnt文件取代std.lnt文件。如果選擇“是”將會(huì)用std_x.lnt文件的內(nèi)容覆蓋std.lnt文件的內(nèi)容,使得當(dāng)前創(chuàng)建的配置選項(xiàng)成為以后創(chuàng)建新的配置文件時(shí)的缺省配置。通常我們選擇“否”繼續(xù)下一步:

圖4.13 生成全局代碼檢查選項(xiàng)文件OPTIONS.LNT
接下來(lái)將會(huì)準(zhǔn)備產(chǎn)生一個(gè)控制全局編譯信息顯示情況的選項(xiàng)文件OPTIONS.LNT,該文件的產(chǎn)生方式有兩種,一種是安裝程序?qū)讉€(gè)核心選項(xiàng)逐一解釋并提問(wèn)你是否取消該選項(xiàng),如果你選擇取消,則會(huì)體現(xiàn)在OPTIONS.LNT文件中,具體體現(xiàn)方式是在該類(lèi)信息編碼前加-e,后面有一系列逐一選擇核心選項(xiàng)的過(guò)程。如果選擇第二種選擇方式,安裝文件會(huì)先生成一個(gè)空的OPTIONS.LNT文件,等你以后在實(shí)際應(yīng)用時(shí)加入必要的選項(xiàng)。這里選擇“No”選項(xiàng),即不取消這些選項(xiàng),然后單擊“下一步”:

圖4.14 選擇所支持的集成開(kāi)發(fā)環(huán)境
接著選擇所支持的集成開(kāi)發(fā)環(huán)境選項(xiàng),可選多個(gè)或一個(gè)也不選,PC-Lint提供了集成在多種開(kāi)發(fā)環(huán)境中工作的功能,例如可集成在VC、BC、Source Insight中。這里我們選擇Microsift Visual C++ 6.0,這樣env-v6.lnt就會(huì)被拷貝到配置路徑中。然后單擊“下一步”:

圖4.15 選擇LIN.BAT文件的使用方式
安裝程序會(huì)生成一個(gè)LIN.BAT文件,該文件是運(yùn)行PC-Lint的批處理文件,為了使該文件能在任何路徑下運(yùn)行,安裝程序提供了兩種方法供你選擇。第一種方法是讓你選擇把LIN.BAT拷貝到任何一個(gè)PATH目錄下。第二種方法是生成一個(gè)LSET.BAT文件,在每次使用PC-LINT前先運(yùn)行它來(lái)設(shè)置路徑,或者把LSET.BAT文件的內(nèi)容拷貝到AUTOEXEC.BAT文件中。建議選擇第一種方法,指定的目錄為當(dāng)前PC-Lint的安裝目錄。我們選擇第一種方式:“copy LIN.BAT to one of my PATH directory”,然后單擊“下一步”輸入PATH目錄:

圖4.16 指定PATH目錄
輸入安裝目錄C:\PCLint8作為PATH目錄,然后單擊“下一步”按鈕進(jìn)入最后的確認(rèn)窗口:

圖4.17 確認(rèn)完成配置
到此就完成了PC-Lint的安裝配置工作,單擊“完成”按鈕就可以使用PC-Lint了。以上配置過(guò)程中在配置路徑下產(chǎn)生的多個(gè)*.lnt文件,除了 std.lnt、std_x.lnt和option.lnt為配置向?qū)桑渌點(diǎn)o-xxx.lnt、lib-xxx.lnt、env- xxx.lnt均是從原始安裝目錄中拷貝出來(lái)的,在這個(gè)目錄下還有其它PCLint所支持的編譯器、庫(kù)及集成開(kāi)發(fā)環(huán)境的lnt配置文件,所有的lnt文件均為文本文件。 上面的配置方法適合于剛開(kāi)始接觸PC-lint時(shí)使用,對(duì)于熟練的使用者可以直接編輯、編寫(xiě)各*.lnt配置文件安成上面的配置工作,或者定制出更適合自己使用的配置環(huán)境。
4.2 PC-Lint與常用開(kāi)發(fā)工具的集成
PC-Lint的使用方法很簡(jiǎn)單,可以用命令行方式進(jìn)行,也可以集成到開(kāi)發(fā)環(huán)境中,下面就分別介紹這些用法
4.2.1 使用命令行方式
命令行的使用方式是PC-lint最基本的使用方式,也是其他各種集成使用方式的基礎(chǔ),通過(guò)命令行可以完成PC-lint的全部代碼分析工作。PC-lint的命令行有下列形式: Lint-nt option file1 [file1 file3 …] 其中的Lint-nt是PC-lint在Windows NT/2000/XP平臺(tái)上的可執(zhí)行程序Lint-nt.exe,它完成PC-lint的基本功能;option代表PC-lint可接受的各種選項(xiàng),這是PC-lint最為復(fù)雜的部分,它的選項(xiàng)有300多種,可以分為:錯(cuò)誤信息禁止選項(xiàng)、變量類(lèi)型大小選項(xiàng)、冗余信息選項(xiàng)、標(biāo)志選項(xiàng)、輸出格式選項(xiàng)和其他選項(xiàng)等幾類(lèi),這些選項(xiàng)在本文的第三部分已經(jīng)介紹過(guò)了;file為待檢查的源文件。 另外值得注意的一點(diǎn)是,在命令行中可以加入前面提到的*.lnt配置文件名,并可以把它看作是命令行的擴(kuò)展,其中配置的各種選項(xiàng)和文件列表,就和寫(xiě)在命令行中具有一樣的效果。
4.2.2 PC-Lint與Visual C++集成開(kāi)發(fā)環(huán)境(IDE)集成
在所有集成開(kāi)發(fā)環(huán)境中,PC-Lint 8.0對(duì)VC++6和VC++7.0的支持是最完善的,甚至支持直接從VC的工程文件(VC6是*.dsp,VC7是*.vcproj)導(dǎo)出對(duì)應(yīng)工程的. Lnt文件,此文件包含了工程設(shè)置中的預(yù)編譯宏,頭文件包含路徑,源文件名,無(wú)需人工編寫(xiě)工程的.Lnt文件。 PC-Lint與VC集成的方式就是在VC的集成開(kāi)發(fā)環(huán)境中添加幾個(gè)定制的命令,添加定制命令的方法是選擇“Tools”的“Customize...” 命令,在彈出的Customize窗口中選擇“Tools”標(biāo)簽,在定制工具命令的標(biāo)簽頁(yè)中添加定制命令。首先要為VC的集成開(kāi)發(fā)環(huán)境添加一個(gè)導(dǎo)出當(dāng)前工程的.Lnt配置文件的功能,導(dǎo)出.Lnt文件的命令行是: lint-nt.exe +linebuf $(TargetName).dsp>$(TargetName).lnt 參數(shù)+linebuf表示加倍行緩沖的大小,最初是600 bytes,行緩沖用于存放當(dāng)前行和你讀到的最長(zhǎng)行的信息。$(TargetName)是VC集成開(kāi)發(fā)環(huán)境的環(huán)境變量,表示當(dāng)前激活的Project名字,注意要選中“Use Output Window”選項(xiàng),這樣PC-Lint就會(huì)將信息輸出到Output窗口中。填寫(xiě)效果如圖4.18所示:

圖4.18 添加導(dǎo)出項(xiàng)目.Lnt文件的定制命令
接著添加一個(gè)檢查當(dāng)前文件的定制命令,檢查文件的命令行為: lint-nt.exe -i"C:\PCLint8" -u std_g.lnt env-vc6.lnt "$(FileName)$(FileExt)" 第一個(gè)參數(shù)-i"C:\PCLint8"為PC-Lint搜索*.lnt文件的目錄,這里就是我們的配置路徑。std_g.lnt是為VC編譯環(huán)境定制的配置文件,$(FileName)和$(FileExt)是VC集成開(kāi)發(fā)環(huán)境的環(huán)境變量,"$(FileName)$(FileExt)"表示當(dāng)前文件的文件名。和導(dǎo)出.Lnt命令一樣,這個(gè)命令也要使用VC集成環(huán)境的Output窗口輸出檢查信息,所以要選中“Use Output Window”選項(xiàng),如圖4.19所示:
 圖4.19 添加檢查當(dāng)前文件文件的定制命令
最后要添加一個(gè)檢查整個(gè)工程的定制命令,檢查整個(gè)工程的命令行是: lint-nt.exe +ffn -i"C:\PCLint8" std_g.lnt env-vc6.lnt $(TargetName).lnt>$(TargetName).chk 這個(gè)命令的結(jié)果就是將整個(gè)工程的檢查結(jié)果輸出到與工程同名的.chk文件中。參數(shù)中+ffn表示Full File Names,可被用于控制是否使用的完整路徑名稱(chēng)表示。 下面就以一個(gè)簡(jiǎn)單的例子介紹一下如何在VC集成開(kāi)發(fā)環(huán)境中使用PC-Lint。首先新建一個(gè)“Win32 Console Application”類(lèi)型的簡(jiǎn)單工程(輸出“Hello World”),然后將本文第二章引用的例子代碼添加到工程的代碼中,最后將這個(gè)工程代碼所倚賴(lài)的包含目錄手工添加到配置文件中,因?yàn)榇a檢查要搜索 stdafx.h這個(gè)預(yù)編譯文件,所以本例要手工添加工程代碼所在的目錄。本文的例子生成的配置文件是std_g.lnt,用文本文件打開(kāi) std_g.lnt,在文件中添加一行: -iC:\unzipped\test “C:\unzipped\test”就是例子工程所在的目錄(stdafx.h就在這個(gè)目錄)。如果你的工程比較龐大,有很多頭文件包含目錄,就需要將這些目錄一一添加到配置文件。在確保代碼輸入沒(méi)有錯(cuò)誤之后(有錯(cuò)誤頁(yè)沒(méi)關(guān)系,PC-Lint會(huì)檢查出錯(cuò)誤),就可以開(kāi)始代碼檢查了。例子工程,打開(kāi)要檢查的代碼文件,本例是test.cpp,然后選擇 “Tools”菜單下的“PC_LINT 8.0 Check Current File”命令,Output窗口輸出對(duì)本文件的檢查結(jié)果,如圖4.20所示:
 圖4.20 檢查結(jié)果
4.2.3 PC-Lint與source insight集成
PC-Lint與source insight的集成也是通過(guò)添加定制命令實(shí)現(xiàn)的,從“Options”菜單中選擇“Custom Commands”命令項(xiàng)。點(diǎn)擊“Add…”按鈕,如圖4.21所示,在彈出的“Custom Commands”窗口中完成以下輸入: 在Name欄中輸入“PC-lint Check Current File”,原則上這個(gè)名稱(chēng)可以隨便起,只要你能搞清楚它的含義就可以了; 在Run欄中輸入“C:\PcLint\lint-nt -u -iC:\PcLint\Lint std_f env-si %f”其中C:\PcLint是你PC-LINT的安裝目錄,std_f表示為Source Insight定制的配置文件std_f.lnt; 在Output欄中選擇“Iconic Window”、“Capture Output”選項(xiàng); 在Control欄中選擇“Save Files First”; 在Source Links in Output欄中選擇“Parse Links in Output”、“File,then Line”; 在Pattern欄中輸入“^\([^ ]*\) \([0-9]+\)”;
 圖4.21 在Source Insight中添加定制命令
命令添加完成后就可以點(diǎn)擊“Run”按鈕就可以對(duì)當(dāng)前文件執(zhí)行PC-Lint檢查。為了方便使用,還可以點(diǎn)擊“Menu...”按鈕將這個(gè)定制命令添加到Source Insight的菜單中。
4.2.4 PC-Lint與UltraEdit集成
在UltraEdit中集成PC-Lint的方法和Source Insight類(lèi)似,也是添加一個(gè)定制命令菜單,具體實(shí)現(xiàn)方法是先單擊UltraEdit的“高級(jí)”菜單中的“工具配置”命令,如圖4.22所示,在打開(kāi)的配置窗口中依次輸入以下內(nèi)容: 在“菜單項(xiàng)目名”欄輸入“PC-lint Check Current File”; 在“命令行”欄輸入以下命令:C:\PCLint\lint-nt –u -iC:\PCLint std env-si %f 其中,C:\PCLint是PC-Lint的安裝目錄,使用std.lnt中的配置,由于UltraEdit和Source Insightde 的檢查環(huán)境類(lèi)似,所以借用env-si中的環(huán)境配置; 在“工作目錄”欄輸入以下路徑:E:\code,這是代碼所在目錄; 選中“先保存所有文件”選項(xiàng); 在“命令輸出”欄中,選中“輸出到列表”和“捕捉輸出”兩個(gè)選項(xiàng); 點(diǎn)“插入”將命令行插入U(xiǎn)ltraEdit的菜單中;

圖4.22 在UltraEdit中添加定制命令
此時(shí)在UltraEdit的“高級(jí)”菜單中會(huì)增加一個(gè)“PC-lint Check Current File”菜單,點(diǎn)擊該菜單即可對(duì)當(dāng)前文件執(zhí)行PC-lint檢查。
五 總結(jié)
軟件除錯(cuò)是軟件項(xiàng)目開(kāi)發(fā)成本和延誤的主要因素,PC-lint能夠幫你在程序動(dòng)態(tài)測(cè)試之前發(fā)現(xiàn)編碼錯(cuò)誤,降低軟件消除錯(cuò)誤的成本。使用PC-Lint在代碼走讀和單元測(cè)試之前進(jìn)行檢查,可以提前發(fā)現(xiàn)程序隱藏錯(cuò)誤,提高代碼質(zhì)量,節(jié)省測(cè)試時(shí)間。另外,使用PC-lint的編碼規(guī)則檢查,可以有效地規(guī)范軟件人員的編碼行為。如果能夠在軟件開(kāi)發(fā)過(guò)程中有效地使用PC-lint代碼檢查工具,將大大地提高代碼質(zhì)量,降低軟件成本。
參考文獻(xiàn)
[1] Gimpel Software. Reference Manual for PC-lint/FlexeLint. July,2001 [2] PC-Lint選項(xiàng)詳解
附錄一 PC-Lint 重要文件說(shuō)明
Msg.txt :解釋告警的內(nèi)容。 options.lnt :反映全局編譯信息顯示情況的選項(xiàng)文件,通常需要添加自定選項(xiàng)以使代碼檢查更為嚴(yán)格。 env-xx.lnt :講述如何將PC-lint與對(duì)應(yīng)的編輯環(huán)境結(jié)合起來(lái),xx是si表示是為Source Insight配置的檢查環(huán)境,xx是vc6則表示是為Visual C++ 6.0準(zhǔn)備的檢查環(huán)境。 co-xxx.lnt :選定的編譯器與庫(kù)選項(xiàng)。 std.lnt :標(biāo)準(zhǔn)配置文件,包含內(nèi)存模型等全局性東西。 lib-xxx.lnt :庫(kù)類(lèi)型的列表,包括標(biāo)準(zhǔn)C/C++庫(kù),MFC庫(kù),OWL庫(kù)等等。 au-xxx.LNT :C++編程提出過(guò)重要建議的作者,選擇某作者后,他提出的編程建議方面的選項(xiàng)將被打開(kāi)。
附錄 二 錯(cuò)誤信息禁止選項(xiàng)說(shuō)明
命令格式 說(shuō)明 代碼中的舉例 -e# 隱藏某類(lèi)錯(cuò)誤 /*lint -e725 */ -e(#) 隱藏下一表達(dá)式中的某類(lèi)錯(cuò)誤 /*lint –e(534) */ printf(“it’s all”); !e# 隱藏本行中的錯(cuò)誤 /*lint !e534*/ printf(“it’s all”); -esym(#, Symbol) 隱藏有關(guān)某符號(hào)的錯(cuò)誤 /*lint –esym(534, printf)*/ printf(“it’s all”); -elib(#) 隱藏頭文件中的某類(lèi)錯(cuò)誤 /*lint –elib(129) */ #include “r01.h” -efunc(#, <func>) 隱藏某個(gè)函數(shù)中的特定錯(cuò)誤 /*lint –efunc(534, mchRelAll)*/ unsigned int mchRelAll(mchHoData *pHoData) { printf(“it’s all”); }
附錄 三 PC-Lint檢測(cè)中的常見(jiàn)錯(cuò)誤
錯(cuò)誤編碼 錯(cuò)誤說(shuō)明 舉例 40 變量未聲明 506 固定的Boolean值 char c=3; if(c<300){} 525 縮排格式錯(cuò)誤 527 無(wú)法執(zhí)行到的語(yǔ)句 if(a > B) return TRUE; else return FALSE; return FALSE; 529 變量未引用 檢查變量未引用的原因 530 使用未初始化的變量 534 忽略函數(shù)返回值 539 縮排格式錯(cuò)誤 545 對(duì)數(shù)組變量使用& char arr[100], *p; p=&arr; 603 指針未初始化 void print_str(const char *p); … char *sz; print_str(sz); 605 指針能力增強(qiáng) void write_str(char *lpsz); … write_str(“string”); 613 可能使用了空指針 616 在switch語(yǔ)句中未使用break; 650 比較數(shù)值時(shí),常量的范圍超過(guò)了 if( ch == 0xFF ) ... 變量范圍 713 把有符號(hào)型數(shù)值賦給了無(wú)符號(hào)型 數(shù)值 715 變量未引用 725 Indentation錯(cuò)誤 734 在賦值時(shí)發(fā)生變量越界 int a, b, c; … c=a*b; 737 無(wú)符號(hào)型變/常量和有變量型 變/常量存在于同一個(gè)表達(dá)式中。 744 在switch語(yǔ)句中沒(méi)有default 752 本地聲明的函數(shù)未被使用 762 函數(shù)重復(fù)聲明 774 Boolean表達(dá)式始終返回真/假 char c; if(c < 300)
PC-Lint的lnt文件、配置方法,適用于VC++6.0與editplus
引用 |
PC-Lint是一個(gè)歷史悠久,功能異常強(qiáng)勁的靜態(tài)代碼檢測(cè)工具,適用于C、C++語(yǔ)言。它的使用歷史可以追溯到計(jì)算機(jī)編程的遠(yuǎn)古時(shí)代(30多年以前)。經(jīng)過(guò)這么多年的發(fā)展,它不但能夠監(jiān)測(cè)出許多語(yǔ)法邏輯上的隱患,而且也能夠有效地幫你提出許多程序在空間利用、運(yùn)行效率上的改進(jìn)點(diǎn),在很多專(zhuān)業(yè)級(jí)的軟件公司,比如Microsoft, PC-Lint檢查無(wú)錯(cuò)誤無(wú)警告是代碼首先要過(guò)的第一關(guān),我個(gè)人覺(jué)得,對(duì)于小公司和個(gè)人開(kāi)發(fā)而言,PC-Lint也非常重要,因?yàn)榛陂_(kāi)發(fā)成本考慮,小公司和個(gè)人往往不能拿出很多很全面的測(cè)試,這時(shí)候,PC-Lint的強(qiáng)勁功能可以很好地提高軟件的質(zhì)量。
|
在此,我提供了一個(gè)適合于VC++的PC-LINT的選項(xiàng)文件, 適用于VC++,也適用于MFC工程。主要是去掉了很多無(wú)用的警告,以免用起來(lái)太費(fèi)勁。 點(diǎn)擊下載特別注意,這個(gè)lnt文件還需要根據(jù)你自己的VC頭文件目錄修改一下里面的頭文件目錄。 在VC中,可以很方便的把PC-LINT集成進(jìn)去,方法如下圖:  在EDITPLUS中,也可以很方便的集成,方法如下圖  集成之后,可以在打開(kāi)文件后,直接點(diǎn)擊工具進(jìn)行檢查,并對(duì)錯(cuò)誤信息進(jìn)行處理,使得用PC-LINT檢查就像用編譯器編譯一樣。 另外,我也做了vxworks下的lnt文件,使用了MISRA 2004嵌入式規(guī)范,也在此提供 下載吧,就不再另行說(shuō)明了。 另外, 如果你沒(méi)有PC-LINT的話(huà),可以從 這里下載8.0老版 希望大家喜歡
|