天天看點

《C語言程式設計初學者指南》一1.8 調試C程式

本節書摘來自異步社群《c語言程式設計初學者指南》一書中的第1章,第1.8節,作者【美】keith davenport(達文波特) , m1ichael vine(維恩),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

如果你的程式編譯了,然後退出了或在執行中出現異常,程式中一定存在一個錯誤(一個bug)。我們将要花費很多的時間來找到并删除這些bug。本小節介紹了幫助你開始這一工作的一些技巧。然而,請記住,調試是計算機科學,同樣也是一門藝術,當然,你的程式設計實踐越多,調試也就變得越容易!往往一個程式編譯和執行得很好,但總是産生你意料之外的或者不想要的結果。例如,如下的程式的編譯和執行沒有錯誤,但是輸出卻是無法讀懂的,或者說不是我所期望的,其輸出如圖1.11所示。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.11 錯誤的格式化

你能看到格式有什麼問題嗎?漏掉了什麼,應該在哪裡進行修改?下一段代碼及其輸出如圖1.12所示,它通過放置相應的轉義序列而改正了格式。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.12 通過在需要的地方添加n和\轉義序列,改正了格式

格式問題是程式設計初學者經常遇到的問題。通常,可以通過熟練使用printf()函數和各種轉義序列來快速地解決這些問題。

另一類常見的bug是邏輯錯誤,包括循環在期望退出的時候沒有退出、數學計算公式的錯誤或者可能是一次有瑕疵的相等性測試(條件)。調試邏輯錯誤的第一個步驟是,找到程式bug存在的第一行代碼。做到這一點的一種方式是使用列印語句,也就是在整個代碼中分散地使用printf()函數。例如,可以在源代碼中做一些如下的事情:

fflush()函數確定了printf語句的結果會立即發送到螢幕,并且如果你想要使用printf()進行調試的話,應該使用fflush()函數。傳遞給fflush()函數的stdout參數是标準輸出,這通常是計算機螢幕。

在将發生邏輯錯誤的地方的範圍縮窄到代碼行或函數之後,下一步就是搞清楚你的變量在彼時的值。還是可以使用printf()函數來列印出變量值,這對于确定非正常的程式行為的源頭有很大的幫助。第2章将詳細介紹使用printf()函數來顯示變量值。

記住,當你修複了任何的bug之後,必須重新編譯程式,運作它,并且如果必要的話,再次進行調試。

作為程式員新手,你經常會遇到的是編譯錯誤,而不是邏輯錯誤,而編譯錯誤通常是文法問題所導緻的,例如漏掉了辨別符和終結符,或者使用了無效的指令、轉義序列或注釋語句塊等文法問題。

調試編譯錯誤可能會令人沮喪,特别是當你在計算機螢幕上看到50條或者更多的錯誤的時候。需要記住的重要的一點是,程式開始處的一個錯誤,可能在編譯的時候導緻一系列層疊性的錯誤。是以,開始調試編譯錯誤的最好的地方,就是錯誤清單中的第一個錯誤。在接下來的幾個小節中,我們将介紹在剛開始編寫c程式的時候的一些較為常見的編譯錯誤。

1.8.1 常見錯誤之1:漏掉程式語句塊辨別符

如果忘記了插入一個開始或對應的結束程式語句塊的辨別符({和}),你将會看到如圖1.13所示的錯誤消息。在下面的示例中,我們故意在main()函數名的後面,漏掉開始程式語句塊辨別符({}。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.13 由于漏掉了程式語句塊辨別符而導緻的錯誤

由于忘記使用開始程式語句塊辨別符({),導緻了很多的錯誤,如圖1.13所示。當調試編譯錯誤的時候,記住要從第1個錯誤開始,如下所示,它告訴你就在printf()函數之前有一個錯誤。你将會發現,解決了第1個錯誤之後,也就更正了很多甚至是所有剩下的錯誤。

技巧

 

為了幫助你找到一個錯誤的位置,編譯器試圖向你顯示捕獲到錯誤的代碼的行号。在前面的示例中,hello.c:5:是告訴你在hello.c源代碼的第 5 行捕獲到了錯誤。注意,編譯器在統計行号的時候,将空行和代碼行都算在内。

盡管編譯器錯誤指向了printf開始處,但重要的是要知道,可能并不是printf而是在它之前的某處出現了錯誤,在這個例子中,是因為漏掉了語句塊辨別符。

1.8.2 常見錯誤之2:漏掉語句終結符

圖1.13展示了由幾個常見場景所導緻的一條常見錯誤消息。這種類型的錯誤可能會由于幾個原因而導緻。除了漏掉程式語句塊的縮進,漏掉了語句終結符(分号)也可能會導緻解析錯誤。

圖1.14展示了如下程式中的一個bug。你能看出這個bug隐藏在哪裡嗎?

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.14 漏掉終結符導緻的錯誤

由于c編譯器不能夠确定一條程式語句(如一條列印語句)到哪裡結束,導緻了解析錯誤。在圖1.14所示的例子中,c編譯器(gcc)告訴你,它期望在第6行的return語句之前有一個分号(語句終結符)。

1.8.3 常見錯誤之3:無效的預處理器指令

如果輸入了一條無效的預處理器指令,例如,把庫的名稱拼寫錯了,你将會接收到一條如圖1.15所示的錯誤消息。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.15 無效的預處理器指令導緻錯誤

在如下的程式語句塊中,預處理器指令中的庫名稱拼寫錯了,導緻了如圖1.15所示的錯誤消息。你能看出錯誤嗎?

由于不存在庫檔案sdio.h,導緻了這個錯誤。标準輸入輸出庫的名稱應該是stdio.h。

1.8.4 常見錯誤之4:無效的轉義序列

在使用轉義序列的時候,常常會使用無效的字元或者無效的字元序列。例如,圖1.16展示了一個無效的轉義序列所導緻的錯誤。gcc 編譯器更為具體地指出了這個錯誤,如圖 1.16所示。特别是,它指出錯誤在第7行,并且指明這是一個未知的轉義序列。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.16 無效的轉義序列導緻的錯誤

你能夠找出如下的程式中的無效的轉義序列嗎?

用諸如n這樣的一個有效的序列,來替代無效的轉義序列m,就可以改正這個問題。

1.8.5 常見錯誤之5:無效的注釋語句塊

正如本章前面的1.3節所提到的,無效的注釋語句塊也會導緻編譯錯誤,如圖1.17所示。

《C語言程式設計初學者指南》一1.8 調試C程式

圖1.17 無效的注釋語句塊導緻錯誤

對于注釋語句塊進行一個簡單的修改,如下所示,就可以解決這個問題,并能夠讓程式成功地編譯。

繼續閱讀