C語言編程常見問題解答之調(diào)試

字號:

調(diào)試(debugging)是指去掉程序中的錯誤(通常被稱為bugs)的過程。一個錯誤可能非常簡單,例如拼錯一個單詞或者漏掉一個分號;也可能比較復(fù)雜,例如使用一個指向并不存在的地址的指針。無論錯誤的復(fù)雜程度如何,掌握正確的調(diào)試方法都能使程序員受益匪淺。
     11.1 如果我運(yùn)行的程序掛起了,應(yīng)該怎么辦?
     當(dāng)你運(yùn)行一個程序時會有多種原因使它掛起,這些原因可以分為以下4種基本類型:
     (1)程序中有死循環(huán);
     (2)程序運(yùn)行的時間比所期望的長;
     (3)程序在等待某些輸入信息,并且直到輸入正確后才會繼續(xù)運(yùn)行;
     (4)程序設(shè)計(jì)的目的就是為了延遲一段時間,或者暫停執(zhí)行。
     在討論了因未知原因而掛起的程序的調(diào)試技巧后,將逐個分析上述的每種情況。
     調(diào)試那些因未知原因而掛起的程序是非常困難的。你可能花費(fèi)了很長的時間編寫一個程序,并努力確保每條代碼都準(zhǔn)確無誤,你也可能只是在一個原來運(yùn)行良好的程序上作了一個很小的修改,然而,當(dāng)你運(yùn)行程序時屏幕上卻什么也沒有顯示。如果你能得到一個錯誤的結(jié)果,或者部分結(jié)果,你也許知道應(yīng)該作些什么修改,而一個空白的屏幕實(shí)在令人沮喪,你根本不知道錯在哪里。
     在開始調(diào)試這樣一個程序時,你應(yīng)該先檢查一下程序結(jié)構(gòu),然后再按執(zhí)行順序依次查看程序的各個部分,看看它們是否能正確運(yùn)行。
     例如,如果主程序只包含3個函數(shù)調(diào)用——A()、B()和C(),那么在調(diào)試時,你可以先檢查函數(shù)A()是否把控制權(quán)返回給了主程序。為此,你可以在調(diào)用函數(shù)A()的語句后面加上exit()命令,也可以用注釋符把對函數(shù)B()和C()的調(diào)用括起來,然后重新編譯并運(yùn)行這個程序。
     注意:通過調(diào)試程序(debugger)也可以做到這一點(diǎn),然而上述方法是一種很傳統(tǒng)的調(diào)試方法。調(diào)試程序是一個程序,它的作用是讓程序員能夠觀察程序的運(yùn)行情況、程序的當(dāng)前運(yùn)行行號、變量的值,等等。
     此時你將看到函數(shù)A()是否將控制權(quán)返回給了主程序——如果該程序運(yùn)行并退出,你可以判斷是程序的其它部分使程序掛起。你可以用這種方法測試程序的每一部分,直到發(fā)現(xiàn)使程序掛起的那一部分,然后集中精力修改相應(yīng)的函數(shù)。
     有時,情況會更復(fù)雜一些。例如,使程序掛起的函數(shù)本身是完全正常的,問題可能出在該函數(shù)從別的地方得到了一些錯誤的數(shù)據(jù)。這時,你就要檢查該函數(shù)所接受的所有的值,并找出是哪些值導(dǎo)致了錯誤操作。
     技巧:監(jiān)視函數(shù)是調(diào)試程序的出色功能之一。
     分析下面這個簡單的例子將幫助你掌握這種技巧的使用方法:
    #include
    #include
    /*
     * Declare the functions that the main function is using
     */
    int A(), B(int), C(int, int);
    /*
     * The main program
     */
    int A(), B(), C(); /*These are functions in some other
     module * /
    int main()
    {
     int v1, v2, v3;
     v1 = A();
     v2 = B(v1);
     v3 = C(v1, v2);
     printf ("The Result is %d. \n" , v3);
     return(0) ;
    }
     你可以在調(diào)用函數(shù)A()的語句后輸出變量v1的值,以確認(rèn)它是否在函數(shù)B()所能接受的值的范圍之內(nèi),因?yàn)榧词故呛瘮?shù)B()使程序掛起,它本身并不一定就有錯,而可能是因?yàn)楹瘮?shù)A()給了函數(shù)B()一個并非它所期望的值。
     現(xiàn)在,已經(jīng)分析了調(diào)試“掛起”的程序的基本方法,下面來看看一些使程序掛起的常見錯誤。
     死循環(huán)
     當(dāng)你的程序出現(xiàn)了死循環(huán)時,機(jī)器將無數(shù)次地執(zhí)行同一段代碼,這種操作當(dāng)然是程序員所不希望的。出現(xiàn)死循環(huán)的原因是程序員使程序進(jìn)行循環(huán)的判斷條件永遠(yuǎn)為真,或者使程序退出循環(huán)的判斷條件永遠(yuǎn)為假。下面是一個死循環(huán)的例子:
    /* initialize a double dimension array */
    for (a = 0 ; a < 10; ++a )
    {
     for(b = 0; b<10; ++a)
     {
     array[a][b]==0;
     }
    }
     這里的問題是程序員犯了一個錯誤(事實(shí)上可能是鍵入字母的錯誤),第二個循環(huán)本應(yīng)在變量b增加到10后結(jié)束,但是卻從未讓變量b的值增加!第二個for循環(huán)的第三部分增加變量a的值,而程序員的本意是要增加變量b的值。因?yàn)閎的值將總是小于10,所以第二個for循環(huán)會一直運(yùn)行下去。
     怎樣才能發(fā)現(xiàn)這個錯誤呢?除非你重新閱讀該程序并注意到變量b的值沒有增加,否則你不可能發(fā)現(xiàn)這個錯誤。當(dāng)你試圖調(diào)試該程序時,你可以在第二個for循環(huán)的循環(huán)體中加入這樣一條語句:
     printf(" %d %d %d\n" , a , b , array[a][b]) ;
    這條語句的正確輸出應(yīng)該是:
     0 0 0
     0 1 0
     (and eventually reaching)
     9 9 0
    但你實(shí)際上看到的輸出卻是:
     0 0 0
     1 0 0
     2 0 0
     ...
    你所得到是一個數(shù)字序列,它的第一項(xiàng)不斷增加,但它本身永遠(yuǎn)不會結(jié)束。用這種方法輸出變量不僅可以找出錯誤,而且還能知道數(shù)組是否由所期望的值組成。這個錯誤用其它方法似乎很難發(fā)現(xiàn)!這種輸出變量內(nèi)容的技巧以后還會用到。