- 程序員修煉之道:程序設計入門30講
- 呂云翔 傅義主編
- 2232字
- 2019-08-08 18:36:05
5.如何調試程序?
編程遇到bug是令每個程序員頭疼的事情,初學者查找bug的一個常見方式就是在代碼中添加輸出語句,將自己想要觀察的變量的值打印到控制臺上,盡管這是一個非常原始的方法,但很多同學發現這種方法行之有效之后就養成了用這種方式查找bug的習慣。然而每一次都要添加和刪除輸出語句的做法實在是非常笨拙,那么到底應該如何進行調試呢?
本節選擇Java作為示例語言,選擇Eclipse作為集成開發環境介紹調試的方法。即使讀者使用的是其他語言,閱讀本節同樣可以幫助其掌握調試的基本思想。
示例代碼5.1

設置斷點
以下我們將通過示例代碼5.1說明如何調試Java代碼。示例代碼5.1的作用是在控制臺輸出1~100中所有的素數,main函數遍歷1~100,通過調用isPrime函數判斷該數是否為素數,如果是素數就打印到控制臺。isPrime函數判斷一個數是否為素數的方法是,查找該數是否有除了1和自身以外的約數,如果不存在其他約數,則說明該數為素數,函數返回true,否則返回false。
調試的第一步是設置斷點,程序運行到斷點時就會暫停并且進入調試模式。我們可以在代碼中任何自己關心的地方設置斷點,設置的方法是在該行代碼的左邊欄雙擊,之后左邊欄就會出現一個圓點,如圖5.1所示,我們在if語句處設置了斷點。

圖5.1 在代碼第6行設置斷點
當程序運行到第6行if語句處時就會暫停,在此之后想要讓程序繼續執行需要通過單步調試來實現。而程序一旦暫停之后,我們就能觀察特定時刻程序中各個變量的值,這樣我們就不需要通過輸出語句來觀察變量了。如果我們設置的是普通斷點,那么程序在第一次運行到該行代碼處就會暫停,示例中也就是for循環中的第一次循環。我們還可以設置條件斷點,設置的方法是右擊斷點,選擇Breakpoint Properties,如圖5.2所示,我們將Hit Count設置為20,這就表示該循環執行到第20次時才會暫停,在此之前的循環不發生暫停,這就是條件斷點。

圖5.2 通過Hit Count設置條件斷點
我們也可以不設置Hit Count,而是通過條件來實現同樣的作用,使得循環執行到第20次時才暫停,如圖5.3所示。勾選Conditional, Suspend when true,并將條件設置為“i == 20”,表示“i == 20”這一條件為真時程序暫停。

圖5.3 通過Conditional設置條件斷點
開始調試
在設置完斷點之后就可以開始調試了,進入調試的方法是單擊調試按鈕,或者右擊斷點,選擇Debug as→Java Application。Eclipse就會進入調試模式,如圖5.4所示,視圖一共被分為5個區域,對應圖中的序號,分別如下。

圖5.4 調試模式視圖
(1)線程堆棧區域:表示當前線程的堆棧,從中可以看出正在運行的代碼與行號,以及整個調用過程。
(2)變量視圖區域:該區域包括三個視圖,變量視圖顯示當前代碼行中所有可以訪問的實例變量和局部變量,斷點視圖顯示當前代碼的所有斷點位置,而在表達式視圖中,用戶可以對自己感興趣的一些變量進行觀察,也可以增加一些自己設定的表達式對其值進行觀察。
(3)代碼區域:該區域顯示程序代碼。
(4)代碼結構區域:該區域顯示代碼中的各種函數方法。
(5)控制臺區域:該區域顯示控制臺信息,用戶可以打印內容到控制臺。
進入調試后,程序在設置的條件斷點(i == 20)處暫停,讓我們來觀察一下變量視圖區域與控制臺區域。圖5.5顯示了變量視圖區域,在變量視圖中,我們可以觀察到for循環中定義的變量i。由于我們設置了條件斷點,程序暫停時i為20。圖5.6顯示了控制臺區域,我們可以觀察到程序在暫停之前輸出了1~20中的所有素數,輸出符合我們設置的條件斷點。

圖5.5 進入調試后的變量視圖區域

圖5.6 進入調試后的控制臺區域
調試方法
程序暫停之后,就需要通過單步調試讓程序繼續執行下去了,所謂的單步調試,就是每一步只執行程序的一行命令,主要的調試方法如表5.1所示。
表5.1 主要的調試方法、快捷鍵及其含義

接下來通過示例代碼5.1講解上述主要調試方法。首先是Step into,即單步進入,程序暫停后,我們按下快捷鍵F5,程序就會開始一行一行執行,由于遇到了函數isPrime,單步進入會讓我們的執行進入isPrime函數內部,于是程序執行到第13行,如圖5.7所示。

圖5.7 調試從main函數進入isPrime函數內部
接下來可以繼續通過F5鍵單步執行,當程序運行到第17行時,我們可以在變量視圖區域發現新增了變量max,其值為4,如圖5.8所示。

圖5.8 進入isPrime函數內部調試時的變量視圖區域
當程序運行到第17行時,我們通過F7鍵執行Step return,這一調試方法的作用是使得當前所在方法直接執行完畢,因此程序將isPrime方法執行完畢后直接返回main函數第6行,如圖5.9所示。
剛才我們已經學習了Step into的方法,讀者或許在想,調試程序的時候可不可以不進入isPrime方法的內部,而只是在main函數的層面進行單步調試呢?答案是肯定的,這就是Step over。Step over同樣是單步執行程序,但是遇到方法不會進入方法內部。示例代碼5.1中,在第6六行按下F6鍵,程序將在執行完isPrime方法后暫停。

圖5.9 調試從isPrime方法返回main函數內部
Resume和Terminate比較容易理解,Resume表示讓程序繼續執行,直到下一個斷點處才會暫停。Terminate方法表示讓程序終止運行。
Drop to frame調試方法比較特別,該方法可以在當前線程的棧幀中回退,可以退回到當前線程的調用開始處。回退時,在需要回退的線程方法上右擊,選擇Drop to frame。以示例代碼5.1為例,當我們通過F5鍵使程序暫停在isPrime函數中間時,執行Drop to frame則會讓調試重新從isPrime函數的起始處執行,所有內存中變量的值都會回退到函數開始的時候。
其他調試技巧
在調試過程中,我們可以修改變量的值。還是以示例代碼5.1為例,在設置條件斷點后,程序暫停,此時變量i的值為20。我們在變量視圖區域右擊變量i,選擇Change Value,就可以修改變量i的值了,如圖5.10所示。
調試時,我們還可以隨時監測表達式的值,選中代碼中的表達式,右擊選擇Watch,就可以在Expressions視圖中看到該表達式的值了。

圖5.10 在調試過程中修改變量的值