天天看點

《C Primer Plus(第6版)中文版》一2.7 調試程式

本節書摘來自異步社群《c primer plus(第6版)中文版》一書中的第2章,第2.7節,作者 傅道坤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

現在,你可以編寫一個簡單的c程式,但是可能會犯一些簡單的錯誤。程式的錯誤通常叫做bug,找出并修正錯誤的過程叫做調試(debug)。程式清單2.4是一個有錯誤的程式,看看你能找出幾處。

程式清單2.4 nogood.c程式

程式清單2.4中有多處文法錯誤。如果不遵循c語言的規則就會犯文法錯誤。這類似于英文中的文法錯誤。例如,看看這個句子:bugs frustrate be can[6]。該句子中的英文單詞都是有效的單詞(即,拼寫正确),但是并未按照正确的順序組織句子,而且用詞也不妥。c語言的文法錯誤指的是,把有效的c符号放在錯誤的地方。

nogood.c程式中有哪些錯誤?其一,main()函數體使用圓括号來代替花括号。這就是把c符号用錯了地方。其二,變量聲明應該這樣寫:

或者,這樣寫:

其三,main()中的注釋末尾漏掉了/(另一種修改方案是,用//替換/)。最後,printf()語句末尾漏掉了分号。

如何發現程式的文法錯誤?首先,在編譯之前,浏覽源代碼看是否能發現一些明顯的錯誤。接下來,檢視編譯器是否發現錯誤,檢查程式的文法錯誤是它的工作之一。在編譯程式時,編譯器發現錯誤會報告錯誤資訊,指出每一處錯誤的性質和具體位置。

盡管如此,編譯器也有出錯的時候。也許某處隐藏的文法錯誤會導緻編譯器誤判。例如,由于nogood.c程式未正确聲明n2和n3,會導緻編譯器在使用這些變量時發現更多問題。實際上,有時不用把編譯器報告的所有錯誤逐一修正,僅修正第1條或前幾處錯誤後,錯誤資訊就會少很多。繼續這樣做,直到編譯器不再報錯。編譯器另一個常見的毛病是,報錯的位置比真正的錯誤位置滞後一行。例如,編譯器在編譯下一行時才會發現上一行缺少分号。是以,如果編譯器報錯某行缺少分号,請檢查上一行。

語義錯誤是指意思上的錯誤。例如,考慮這個句子:scornful derivatives sing greenly(輕蔑的衍生物不熟練地唱歌)。句中的形容詞、名詞、動詞和副詞都在正确的位置上,是以文法正确。但是,卻讓人不知所雲。在c語言中,如果遵循了c規則,但是結果不正确,那就是犯了語義錯誤。程式示例中有這樣的錯誤:

此處,n3原意表示n的3次方,但是代碼中的n3被設定成n的4次方(n2 = n * n)。

編譯器無法檢測語義錯誤,因為這類錯誤并未違反c語言的規則。編譯器無法了解你的真正意圖,是以你隻能自己找出這些錯誤。例如,假設你修正了程式的文法錯誤,程式應該如程式清單2.5所示:

程式清單2.5 stillbad.c程式

該程式的輸出如下:

如果對簡單的立方比較熟悉,就會注意到625不對。下一步是跟蹤程式的執行步驟,找出程式如何得出這個答案。對于本例,通過檢視代碼就會發現其中的錯誤,但是,還應該學習更系統的方法。方法之一是,把自己想象成計算機,跟着程式的步驟一步一步地執行。下面,我們來試試這種方法。

main()函數體一開始就聲明了3個變量:n、n2、n3。你可以畫出3個盒子并把變量名寫在盒子上來模拟這種情況(見圖2.6)。接下來,程式把5賦給變量n。你可以在标簽為n的盒子裡寫上5。接着,程式把n和n相乘,并把乘積賦給n2。是以,檢視标簽為n的盒子,其值是5,5乘以5得25,于是把25放進标簽為n2的盒子裡。為了模拟下一條語句(n3 = n2 * n2),檢視n2盒子,發現其值是25。25乘以25得625,把625放進标簽為n3的盒子。原來如此!程式中計算的是n2的平方,不是用n2乘以n得到n的3次方。

《C Primer Plus(第6版)中文版》一2.7 調試程式

圖2.6 跟蹤程式的執行步驟

對于上面的程式示例,檢查程式的過程可能過于繁瑣。但是,用這種方法一步一步檢視程式的執行情況,通常是發現程式問題所在的良方。

通過逐漸跟蹤程式的執行步驟,并記錄每個變量,便可監視程式的狀态。程式狀态(program state)是在程式的執行過程中,某給定點上所有變量值的集合。它是計算機目前狀态的一個快照。

我們剛剛讨論了一種跟蹤程式狀态的方法:自己模拟計算機逐漸執行程式。但是,如果程式中有10000次循環,這種方法恐怕行不通。不過,你可以跟蹤一小部分循環,看看程式是否按照預期的方式執行。另外,還要考慮一種情況:你很可能按照自己所想去執行程式,而不是根據實際寫出來的代碼去執行。是以,要盡量忠實于代碼來模拟。

定位語義錯誤的另一種方法是:在程式中的關鍵點插入額外的printf()語句,以監視制定變量值的變化。通過檢視值的變化可以了解程式的執行情況。對程式的執行滿意後,便可删除額外的printf()語句,然後重新編譯。

檢測程式狀态的第3種方法是使用調試器。調試器(debugger)是一種程式,讓你一步一步運作另一個程式,并檢查該程式變量的值。調試器有不同的使用難度和複雜度。較進階的調試器會顯示正在執行的源代碼行号。這在檢查有多條執行路徑的程式時很友善,因為很容易知道正在執行哪條路徑。如果你的編譯器自帶調試器,現在可以花點時間學會怎麼使用它。例如,試着調試一下程式清單2.4。

繼續閱讀