秦振華,牟永敏
(北京信息科技大學(xué) 計(jì)算機(jī)學(xué)院,北京 100101)
軟件復(fù)雜性主要表現(xiàn)在程序的復(fù)雜性,即模塊內(nèi)程序的復(fù)雜性[1]。常見的定量度量軟件復(fù)雜性的方法有代碼行度量法、McCabe度量法和Halstead度量法。
McCabe度量法根據(jù)程序控制流的復(fù)雜程度定量度量程序復(fù)雜程度,這樣度量出的結(jié)果稱為程序的環(huán)形復(fù)雜度。在軟件測(cè)試中,環(huán)形復(fù)雜度用于衡量一個(gè)模塊判定結(jié)構(gòu)的復(fù)雜程度,數(shù)量上表現(xiàn)為獨(dú)立路徑條數(shù),即合理的預(yù)防錯(cuò)誤所需測(cè)試的最少路徑條數(shù),這是為確保所有語句至少執(zhí)行一次而必須進(jìn)行測(cè)試的數(shù)量的上界,也可以理解為覆蓋所有的可能情況最少使用的測(cè)試用例數(shù)[2-4]。在通常情況下,程序環(huán)形復(fù)雜度值越大,說明程序判斷邏輯越復(fù)雜,越容易出錯(cuò),且軟件難以測(cè)試與維護(hù)。大量研究和經(jīng)驗(yàn)表明,環(huán)形復(fù)雜度與軟件模塊中的錯(cuò)誤緊密相關(guān),假如一個(gè)模塊比較復(fù)雜,那么它就容易出錯(cuò),當(dāng)超過了度量的閾值(通常是10),模塊中的錯(cuò)誤數(shù)量也會(huì)隨之急劇增長[5-8]。
目前,計(jì)算程序環(huán)形復(fù)雜度的方法有2種:一種是根據(jù)控制流圖人工計(jì)算出環(huán)形復(fù)雜度,當(dāng)程序比較復(fù)雜時(shí)容易產(chǎn)生錯(cuò)誤;另一種是采用工具自動(dòng)計(jì)算,如SourceMonitor,計(jì)算速度快,但在某些復(fù)雜條件下得出的環(huán)形復(fù)雜度不夠準(zhǔn)確。
針對(duì)上述問題,本文通過對(duì)源程序進(jìn)行預(yù)處理,提取程序中含有控制流信息的關(guān)鍵語句,實(shí)現(xiàn)程序環(huán)形復(fù)雜度的自動(dòng)化計(jì)算。
控制流圖(Control Flow Graph,CFG)是一個(gè)有向圖G=(V,E),其中,V是控制流節(jié)點(diǎn)的集合,E是有向邊的集合。一個(gè)控制流圖有一個(gè)唯一的入口節(jié)點(diǎn)(其入度為0)和一個(gè)唯一的出口節(jié)點(diǎn)(其出度為0)??刂屏鲌D中的節(jié)點(diǎn)V代表程序的語句或表達(dá)式,有向邊E表示語句間的執(zhí)行關(guān)系。令(x,y)∈E,表示控制流圖中的邊x→y,稱x是y的前驅(qū),y是x的后繼。
控制流圖中的節(jié)點(diǎn)可分為2種類型:一種是塊結(jié)構(gòu),即把程序劃分為塊,塊中的語句是連續(xù)的,且只包含簡單語句,不包含分支和循環(huán)等引起程序執(zhí)行順序改變的語句;另一種是程序的每一條語句都單獨(dú)地看成一個(gè)控制流節(jié)點(diǎn)[9-13]。本文繪制的控制流圖節(jié)點(diǎn)采用第一種類型,并且以程序的行號(hào)來表示程序的控制流節(jié)點(diǎn)。
計(jì)算程序的環(huán)形復(fù)雜度有3種方法,以圖1所示控制流圖為例。
圖1 控制流圖
3種方法的具體描述如下:
1)給定控制流圖G的環(huán)形復(fù)雜度CC(G),控制流圖中區(qū)域的數(shù)量F(僅考慮區(qū)域個(gè)數(shù),不考慮邊的方向)。圖中區(qū)域數(shù)量為4(R1、R2、R3、R4),則CC(G)=F=4。
2)給定控制流圖G的環(huán)形復(fù)雜度CC(G),定義為CC(G)=E-N+2,E是控制流圖中邊的數(shù)量,N是控制流圖中節(jié)點(diǎn)的數(shù)量。圖中邊的數(shù)量為E=10,節(jié)點(diǎn)的數(shù)量為N=8,則CC(G)=10-8+2=4。
3)給定控制流圖G的環(huán)形復(fù)雜度CC(G),定義為CC(G)=P+1,P是控制流圖中判定節(jié)點(diǎn)的數(shù)量,判定節(jié)點(diǎn)是只包含一個(gè)條件的節(jié)點(diǎn),從每一個(gè)判定節(jié)點(diǎn)發(fā)出2條或多條邊。圖中判定節(jié)點(diǎn)的數(shù)量為P=3,3個(gè)判定節(jié)點(diǎn)為4、6、8,則CC(G)=3+1=4。
本文針對(duì)C語言程序研究環(huán)形復(fù)雜度的自動(dòng)化計(jì)算,首先分析其結(jié)構(gòu)特點(diǎn)。C語言程序中的語句分為控制語句、函數(shù)調(diào)用語句、表達(dá)式語句、空語句和復(fù)合語句共5種,其中,控制語句分為9種,分別是if-else條件語句、for循環(huán)語句、while循環(huán)語句、do-while循環(huán)語句、continue語句、break語句、switch多分支選擇語句、return語句和goto語句[14-15]。在這里,將if-else語句、if-else if語句、switch-case語句和包含三目運(yùn)算符的語句稱為分支語句,for語句、while語句和do-while語句稱為循環(huán)語句,分支語句和循環(huán)語句將對(duì)應(yīng)于控制流圖中的判定節(jié)點(diǎn)。因此,可以借助程序中的分支和循環(huán)語句來實(shí)現(xiàn)環(huán)形復(fù)雜度的計(jì)算。當(dāng)程序中分支語句或者循環(huán)語句的判別條件為復(fù)合條件時(shí),將復(fù)合條件分解成單個(gè)條件后,再進(jìn)行環(huán)形復(fù)雜度的計(jì)算。下面將分情況討論C語言程序中分支語句和循環(huán)語句對(duì)程序環(huán)形復(fù)雜度的影響。
1)if-else語句
當(dāng)if語句中只有1個(gè)判別條件時(shí),當(dāng)前語句對(duì)應(yīng)的判定節(jié)點(diǎn)的數(shù)量等于1;當(dāng)if語句中有多個(gè)判別條件時(shí),當(dāng)前語句對(duì)應(yīng)的判定節(jié)點(diǎn)的數(shù)量等于if語句中判別條件的個(gè)數(shù)。
2)if-else if語句
對(duì)于if-else if語句,除了考慮if語句的情況外,還需要考慮else if語句的情況,處理方法和if-else語句的處理方法相同,當(dāng)前語句對(duì)應(yīng)的判定節(jié)點(diǎn)的數(shù)量等于判別條件的個(gè)數(shù)。
3)包含三目運(yùn)算符的語句
由于包含三目運(yùn)算符的語句,也是一種特殊的條件語句,因此其處理方法和if-else語句的處理方法一樣,其對(duì)應(yīng)的判定節(jié)點(diǎn)的數(shù)量等于語句中判別條件的個(gè)數(shù)。
4)switch-case語句
對(duì)于switch-case而言,有沒有default語句對(duì)于程序環(huán)形復(fù)雜度的計(jì)算沒有影響,在一般情況下,判定節(jié)點(diǎn)的數(shù)量等于case的個(gè)數(shù)。由于switch-case不用像if-else if那樣遍歷條件分支直到命中條件,而只需訪問對(duì)應(yīng)索引號(hào)的表項(xiàng)從而達(dá)到定位分支的目的。具體地說,switch-case會(huì)生成一份大小(表項(xiàng)數(shù))為最大case常量+1的跳表,程序首先判斷switch變量是否大于最大case常量,若大于,則跳到default分支處理;否則,取得索引號(hào)為switch變量大小的跳表項(xiàng)的地址(即跳表的起始地址+表項(xiàng)大小×索引號(hào)),程序接著跳到此地址執(zhí)行,到此完成了分支的跳轉(zhuǎn)。所以,即使case中沒有break語句,也不會(huì)影響產(chǎn)生的分支數(shù),對(duì)程序環(huán)形復(fù)雜度的計(jì)算也不會(huì)產(chǎn)生影響。但是存在一種特殊情況,即多個(gè)case相連,此時(shí)需要把多個(gè)相連的case當(dāng)成一個(gè)判定節(jié)點(diǎn)進(jìn)行計(jì)算,以文獻(xiàn)[2]中的例子加以說明,如圖2所示。
圖2 多個(gè)case相連的情況
5)while語句
在一個(gè)循環(huán)結(jié)構(gòu)中,循環(huán)條件表達(dá)式和循環(huán)結(jié)束條件共同決定程序的最終走向,結(jié)合這一特點(diǎn),對(duì)于while循環(huán)語句,可以分5種情況進(jìn)行討論:
(1)條件表達(dá)式永真,這里只考慮while(1)這種形式的永真,但是有循環(huán)結(jié)束條件,此時(shí)while語句不是判定節(jié)點(diǎn),如圖3所示。
圖3 while語句的特殊形式
在圖3中,第4行和第6行語句合并為一個(gè)節(jié)點(diǎn),此時(shí)控制流圖中只有一個(gè)判定節(jié)點(diǎn),這與平時(shí)遇見的情況有所不同,需要特殊處理。
(2)條件表達(dá)式永真,但是無循環(huán)結(jié)束條件,即所謂的死循環(huán),此時(shí)while語句是判定節(jié)點(diǎn)。
(3)條件表達(dá)式永假,這里只考慮while(0)這種形式的永假,此時(shí)循環(huán)體內(nèi)的所有判定節(jié)點(diǎn)都不需要計(jì)算,包括while自己。
(4)條件表達(dá)式非永真永假,并且是單個(gè)條件,此時(shí)while語句相當(dāng)于一個(gè)判定節(jié)點(diǎn)。
(5)條件表達(dá)式非永真永假,并且是復(fù)合條件,此時(shí)while語句對(duì)應(yīng)判定節(jié)點(diǎn)的數(shù)量等于while語句中判別條件的個(gè)數(shù)。
6)for語句
for語句的處理方法和while語句的處理方法一樣。
7)do-while語句
do-while語句的處理方法和while語句的處理方法基本一致,唯一的區(qū)別是當(dāng)條件表達(dá)式永假時(shí),while語句的循環(huán)體內(nèi)的所有判定節(jié)點(diǎn)都不需要計(jì)算,而do-while語句的循環(huán)體內(nèi)的判定節(jié)點(diǎn)需要計(jì)算。
環(huán)形復(fù)雜度是代碼復(fù)雜度的一個(gè)指示器,可以利用一些著名的開放源碼工具計(jì)算環(huán)形復(fù)雜度,比如PMD、CheckStyle和JavaNCSS,但是這些工具只是針對(duì)Java程序的,OCLint、Testwell CMT++、Understand、SourceMonitor等工具可以度量C程序的環(huán)形復(fù)雜度。在實(shí)驗(yàn)部分,借助于后2種工具和本文提出的方法進(jìn)行比較。
Understand是一個(gè)SciTools發(fā)行的、商用的靜態(tài)分析工具,可以維護(hù)、測(cè)量和分析源代碼。該工具能夠分析14種語言,包括C/C++、Java、FORTRAN和一些Web編程語言,例如PHP,并且適用于大部分操作系統(tǒng),包括Solaris。該工具可以對(duì)整個(gè)項(xiàng)目的結(jié)構(gòu)、度量值進(jìn)行分析并輸出報(bào)表,能夠?qū)Υa生成多種圖,包括控制流圖、依賴關(guān)系圖、UML類圖等。
SourceMonitor是一款免費(fèi)的軟件,運(yùn)行在Windows平臺(tái)下。它可對(duì)多種語言寫的代碼進(jìn)行度量,包括C、C++、C#、Java、VB、Delphi和HTML,并且針對(duì)不同的語言,輸出不同的代碼度量值。對(duì)于C程序,提供了總行數(shù)、語句數(shù)目、分支語句比例、注釋比例、函數(shù)數(shù)目、平均每個(gè)函數(shù)包含的語句數(shù)目、函數(shù)環(huán)形復(fù)雜度和函數(shù)深度等度量值。
本文提出的研究方法可分為3個(gè)階段:源代碼的預(yù)處理,控制流信息的提取,環(huán)形復(fù)雜度的自動(dòng)化計(jì)算。其中,采用了狀態(tài)機(jī)編程思想刪掉源程序中的注釋來實(shí)現(xiàn)源代碼的預(yù)處理,利用模式匹配的規(guī)則來提取含控制流信息的關(guān)鍵語句,通過分情況處理關(guān)鍵語句來實(shí)現(xiàn)程序環(huán)形復(fù)雜度的自動(dòng)化計(jì)算。
源代碼的預(yù)處理主要是掃描程序中出現(xiàn)的注釋,并將其刪掉。由于注釋信息對(duì)最后環(huán)形復(fù)雜度的計(jì)算沒有影響,并且注釋中的某些信息會(huì)干擾接下來關(guān)鍵語句的提取,因此找出源代碼中的注釋信息,將其從源代碼中刪掉。這里只考慮單行注釋、多行注釋和折行注釋這3種情況,其中,折行注釋是以雙斜杠開頭、反斜杠結(jié)尾,很多編輯器高亮都沒有考慮到這種情況。此處采用了狀態(tài)機(jī)編程思想將源代碼中的注釋刪掉。
在程序中,分支語句和循環(huán)語句最終對(duì)應(yīng)于控制流圖中的判定節(jié)點(diǎn),因此,如何提取程序中的分支和循環(huán)語句顯得尤為重要。此處通過模式匹配的方式提取程序中的關(guān)鍵語句,其中模式匹配規(guī)則如表1所示。
表1 模式匹配規(guī)則
表1中的模式匹配規(guī)則共有6種模式,左側(cè)為模式序號(hào),右側(cè)為模式匹配的規(guī)則。由于計(jì)算的是單個(gè)函數(shù)的環(huán)形復(fù)雜度,因此用模式P1匹配程序中的函數(shù)定義,從而開啟一個(gè)處理單元。分支語句和循環(huán)語句用模式P2、P3、P4和P5進(jìn)行匹配。其中,模式P2匹配if、for和while語句,模式P3匹配switch-case語句,模式P4匹配包含三目運(yùn)算符的語句,模式P5匹配do-while語句和break語句,匹配break語句是為了后續(xù)判斷當(dāng)循環(huán)結(jié)構(gòu)中無循環(huán)條件表達(dá)式時(shí),有無循環(huán)結(jié)束條件。如果循環(huán)體中有循環(huán)結(jié)束條件break語句,那么循環(huán)可以正常結(jié)束,此時(shí)的循環(huán)語句不是判定節(jié)點(diǎn),否則是死循環(huán)結(jié)構(gòu),此時(shí)的循環(huán)語句是判定節(jié)點(diǎn)。模式P6用來進(jìn)行花括號(hào)匹配,左花括號(hào)對(duì)應(yīng)語句塊的開始,右花括號(hào)對(duì)應(yīng)語句塊的結(jié)束,根據(jù)花括號(hào),可以對(duì)源程序進(jìn)行分塊處理。在表1規(guī)則的基礎(chǔ)上,再結(jié)合正則表達(dá)式中的search()方法,就可以將程序中的關(guān)鍵語句信息都提取出來。
經(jīng)過源程序的預(yù)處理和提取關(guān)鍵語句這2步,此時(shí)程序中只剩下分支語句、循環(huán)語句等關(guān)鍵信息,接下來需要根據(jù)這些關(guān)鍵信息,進(jìn)行程序環(huán)形復(fù)雜度的計(jì)算。在當(dāng)前步的處理中需要注意的是:當(dāng)語句中判別條件是復(fù)合條件時(shí),需要把復(fù)合條件分解成單個(gè)條件進(jìn)行計(jì)算。對(duì)于循環(huán)語句中條件表達(dá)式永真、永假的情況,尤其是當(dāng)條件表達(dá)式永真,但是有break語句可以跳出循環(huán),此時(shí)當(dāng)前循環(huán)語句并不能當(dāng)成判定節(jié)點(diǎn)這種情況,都需要進(jìn)行特殊處理。
鑒于此,在當(dāng)前步驟的算法設(shè)計(jì)中采用自頂向下的分析策略,對(duì)各種情況分別編寫相應(yīng)的處理函數(shù),定義一個(gè)棧stack用來進(jìn)行花括號(hào)匹配,定義一個(gè)變量num用來記錄最終環(huán)形復(fù)雜度的值,初始值為0,并用flag標(biāo)志來標(biāo)記循環(huán)語句塊中是否有break語句,flag=0表示沒有,flag=1表示有,用ok標(biāo)志標(biāo)記if語句塊中是否包含break語句,當(dāng)循環(huán)語句塊中嵌套if語句塊,if語句塊中包含break語句,此時(shí)break語句也可以跳出循環(huán),flag=1。然后,從上往下掃描源程序,如果當(dāng)前語句中包含“{”,則進(jìn)行入棧操作,stack.push(“{”);若包含“}”,則進(jìn)行出棧操作,stack.pop();若包含“if”關(guān)鍵字、“switch”關(guān)鍵字、“do”關(guān)鍵字、“for”關(guān)鍵字、“while”關(guān)鍵字、三目運(yùn)算符,則相應(yīng)地調(diào)用count_if()、count_case()、count_do()、count_for()、count_while()、count_three()方法,通過對(duì)應(yīng)的方法計(jì)算代碼塊中相應(yīng)代碼的環(huán)形復(fù)雜度,并把計(jì)算得到的結(jié)果加到變量num中去,接著進(jìn)行下面語句的處理,直到程序結(jié)束。這樣,最終程序環(huán)形復(fù)雜度的值便等于變量num的值。其中,計(jì)算while語句塊中相應(yīng)代碼的環(huán)形復(fù)雜度的算法如下。
算法1cout_while
輸入程序的行號(hào)值
輸出環(huán)形復(fù)雜度值
1.str=the current statement,sum=0,flag=0
2.index=the current statement’s line number value
3.ok=whether the if statement block contains break
4.if conditional expression is equal to 0 then
5. while stack count is not equal to 0 do
6. read the next statement
7. end while
8.else
9. while stack count is not equal to 0 do
10. str=the next statement
11. if str contains if then
12. sum+=count_if(index)
13. if ok is true then
14. ok=false,flag=1
15. end if
16. else if str contains switch then
17. sum+=count_case(index)
18. else if str contains do then
19. sum+=count_do(index)
20. else if str contains while then
21. sum+=count_while(index)
22. else if str contains for then
23. sum+=count_for(index)
24. else if str contains ternary operator then
25. sum+=count_three(index)
26. else if str contains break then
27. flag=1
28. end if
29. end while
30. if conditional expression is equal to 1 then
31. if flag is equal to 0 then
32. sum++
33. end if
34. else
35. sum+=the number of conditions
36. end if
37. end if
38. return sum
在算法1中,對(duì)于while語句塊的處理,需要考慮條件表達(dá)式永真、永假和非永真永假這3種情況,語句塊的開始和結(jié)束對(duì)應(yīng)棧的入棧和出棧操作,當(dāng)棧中元素個(gè)數(shù)為0時(shí),標(biāo)志著當(dāng)前語句塊中內(nèi)容處理完畢。對(duì)于條件表達(dá)式永假的情況,直接順序讀取語句塊中的內(nèi)容即可;對(duì)于永真的情況,需要根據(jù)flag的值來判斷sum的值需不需要加1;對(duì)于非永真永假的情況,sum需要加的值等于while語句中條件的個(gè)數(shù)。
實(shí)驗(yàn)所用操作系統(tǒng)為64位Windows 7旗艦版Service Pack 1,內(nèi)存為4 GB,處理器為Intel(R) CoreTMi7-2820QM CPU @ 2.30 GHz。
實(shí)驗(yàn)中用的測(cè)試用例采用了文獻(xiàn)[15]中的程序,該程序涉及到了注釋、if-else語句、switch-case語句、while語句、三目運(yùn)算符、單個(gè)判別條件和復(fù)合條件,具有一定的代表性。具體測(cè)試用例如下所示。
int main()
{
1. int a,b,max,c,count=0;
2. char operate,flag;
3. while(1)
{
4. flag=getch();
5. printf("第%d次運(yùn)算 ",++count);
//輸入的字符不是小寫字母,退出程序
6. if(flag>='a' && flag<='z')
{
//輸入運(yùn)算數(shù)和運(yùn)算符
7. scanf("%d%c%d",&a,&operate,&b);
8. max=a>b ? a:b;//max記錄最大值
9. switch(operate)
{
10. case'*':
11. c=a*b;
12. break;
13. case'/':
14. if(b==0)
15. c=-1;
else
16. c=a/b;
17. break;
18. default:
19. c=-1;
}
20. printf("結(jié)果為:%d,%d ",max,c);
}
else
{
21. break;
}
}
22. return 0;
}
為了能更好地驗(yàn)證本文提出的環(huán)形復(fù)雜度自動(dòng)化計(jì)算方法,同時(shí)實(shí)現(xiàn)控制流圖的自動(dòng)生成,采用如下方法:利用GCC編譯源代碼生成中間文件,提取并分析中間文件的語句塊信息,找出語句塊之間的關(guān)系,用替換的方法把語句塊之間的關(guān)系轉(zhuǎn)換成行號(hào)之間的關(guān)系,并把結(jié)果輸出到一個(gè).dot文件中去,最后使用WinGraphviz這個(gè)COM組件把最終結(jié)果以控制流圖的形式展示出來[16-18]。
本文系統(tǒng)在自動(dòng)生成控制流圖時(shí),對(duì)于程序中判別條件是復(fù)合條件的情況,將復(fù)合條件拆成單個(gè)條件,并用“行號(hào)_條件標(biāo)號(hào)”這種形式進(jìn)行表示,例如“6_1”,表示第6行第1個(gè)判別條件。對(duì)于三目運(yùn)算符來說,執(zhí)行的語句用“行號(hào)_語句塊標(biāo)號(hào)”這種形式來表示,例如“8_01”,表示執(zhí)行第8行第1個(gè)語句塊中的語句。上述測(cè)試用例所對(duì)應(yīng)的控制流圖如圖4所示。
圖4 測(cè)試用例對(duì)應(yīng)的控制流圖
測(cè)試用例中if(flag>=‘a(chǎn)’&& flag<=‘z’)語句的判別條件是復(fù)合條件,拆成2個(gè)判定節(jié)點(diǎn),max=a>b ? a:b;語句包含三目運(yùn)算符,判別條件是單個(gè)條件,相當(dāng)于一個(gè)判定節(jié)點(diǎn),switch-case中有2個(gè)case,產(chǎn)生2個(gè)判定節(jié)點(diǎn),再加上嵌套的if(b==0)語句,共產(chǎn)生3個(gè)判定節(jié)點(diǎn),而對(duì)于while(1)這種條件表達(dá)式永真的循環(huán)語句,由于循環(huán)體中存在循環(huán)結(jié)束條件,因此while(1)語句不是判定節(jié)點(diǎn)。綜上所述,測(cè)試用例中共有6個(gè)判定節(jié)點(diǎn),程序的環(huán)形復(fù)雜度為7。
根據(jù)圖4可知測(cè)試用例的環(huán)形復(fù)雜度為7,這與圖5得出的實(shí)驗(yàn)結(jié)果一致。所以,本文提出的方法可以準(zhǔn)確地計(jì)算出程序的環(huán)形復(fù)雜度。將系統(tǒng)命名為RcTest工具,與Understand和SourceMonitor工具針對(duì)上述測(cè)試用例計(jì)算得到的結(jié)果進(jìn)行比較,最終結(jié)果如表2所示。
圖5 程序運(yùn)行結(jié)果
工具環(huán)形復(fù)雜度分析RcTest7while(1)語句不是判定節(jié)點(diǎn)Understand7把復(fù)合條件、while(1)語句都當(dāng)成一個(gè)判定節(jié)點(diǎn)SourceMonitor11while(1)、else、default語句都當(dāng)成一個(gè)判定節(jié)點(diǎn)
經(jīng)過大量實(shí)驗(yàn)得出,Understand和SourceMonitor工具在計(jì)算環(huán)形復(fù)雜度時(shí)與RcTest有如下不同:
1)Understand工具
(1)在生成控制流圖和計(jì)算時(shí),把復(fù)合條件當(dāng)成一個(gè)條件進(jìn)行分析;
(2)像循環(huán)語句中while(1)和while(0)這2種特殊的永真和永假形式,并沒有被考慮,在控制流圖中會(huì)畫出while節(jié)點(diǎn),并把while節(jié)點(diǎn)當(dāng)成判定節(jié)點(diǎn)加入到環(huán)形復(fù)雜度中去;
(3)對(duì)于包含三目運(yùn)算符的語句,在控制流圖中當(dāng)成順序語句節(jié)點(diǎn)來處理,在計(jì)算時(shí),會(huì)當(dāng)成一個(gè)判定節(jié)點(diǎn)進(jìn)行計(jì)算,但是沒有考慮復(fù)合條件的情況;
(4)對(duì)于switch-case語句,在一般情況下,當(dāng)case語句塊中沒有break語句時(shí),對(duì)環(huán)形復(fù)雜度的計(jì)算無影響,但是一旦出現(xiàn)多個(gè)case相連這種特殊情況,在生成控制流圖時(shí),會(huì)把多個(gè)相連的case當(dāng)成一個(gè)節(jié)點(diǎn),在計(jì)算時(shí),統(tǒng)計(jì)的是case的個(gè)數(shù)。
2)SourceMonitor工具
(1)將復(fù)合條件拆成多個(gè)單條件進(jìn)行計(jì)算,但是在處理包含三目運(yùn)算符的語句時(shí),沒有考慮復(fù)合條件,只當(dāng)成一個(gè)判定節(jié)點(diǎn);
(2)對(duì)于if-else結(jié)構(gòu)的語句,會(huì)把else當(dāng)成一個(gè)判定節(jié)點(diǎn);
(3)沒有考慮循環(huán)語句中永真和永假的特殊形式;
(4)對(duì)于switch-case語句,在一般情況下,不管有沒有default語句,判定節(jié)點(diǎn)數(shù)等于case的個(gè)數(shù)+1。但是當(dāng)case語句塊中沒有break語句時(shí),當(dāng)前case語句不當(dāng)成判定節(jié)點(diǎn),對(duì)于多個(gè)case相連的情況,會(huì)把多個(gè)相連的case當(dāng)成一個(gè)判定節(jié)點(diǎn)進(jìn)行計(jì)算。
通過比較可知,系統(tǒng)在計(jì)算環(huán)形復(fù)雜度時(shí),相對(duì)于Understand和SourceMonitor工具更加準(zhǔn)確。
下面分析3種工具在計(jì)算時(shí)間開銷上(單位是秒)的差別,分別以104、105、106行代碼為例進(jìn)行比較,最終結(jié)果如表3所示。
表3 3種工具的計(jì)算時(shí)間開銷 s
由表3可知,RcTest工具在計(jì)算效率上比SourceMonitor略高。由于Understand工具會(huì)生成控制流圖等額外信息,因此時(shí)間開銷較大,但是產(chǎn)生的信息比較齊全。
環(huán)形復(fù)雜度是軟件難度度量的一個(gè)重要指標(biāo),本文通過提取源程序中含有控制流信息的關(guān)鍵語句,對(duì)其進(jìn)行分析處理,用于實(shí)現(xiàn)環(huán)形復(fù)雜度的自動(dòng)化計(jì)算。在這種方法下,無需知道源程序的控制流圖,而且通過去掉源程序中一些無關(guān)緊要的信息,只留下影響環(huán)形復(fù)雜度的關(guān)鍵信息,進(jìn)而可以高效、準(zhǔn)確地計(jì)算出程序的環(huán)形復(fù)雜度,可操作性更強(qiáng),更簡單,節(jié)約了測(cè)試時(shí)間,降低了測(cè)試成本。下一步將完善該工具的擴(kuò)展性,使其可以計(jì)算其他語言編寫的程序的環(huán)形復(fù)雜度。