對C語言進行調試的最好方法是什么?

要了解調試程序的最好方法,首先要分析一下調試過程的三個要素:
應該用什么工具調試一個程序?
用什么辦法才能找出程序中的錯誤?
怎樣才能從一開始就避免錯誤?
應該用什么工具調試一個程序?

有經驗的程序員會使用許多工具來幫助調試程序,包括一組調試程序和一些"lint”程序,當然,編譯程序本身也是一種調試工具。

在檢查程序中的邏輯錯誤時,調試程序是特別有用的,因此許多程序員都把調試程序作為基本的調試工具。一般來說,調試程序能幫助程序員完成以下工作:
(1)觀察程序的運行情況
僅這項功能就使一個典型的調試程序具備了不可估量的價值。即使你花了幾個月的時間精心編寫了一個程序,你也不一定完全清楚這個程序每一步的運行情況。如果程序員忘記了某些if語句、函數調用或分支程序,可能會導致某些程序段被跳過或執行,而這種結果并不是程序員所期望的。不管怎樣,在程序的執行過程中,尤其是當程序有異常表現時,如果程序員能隨時查看當前被執行的是那幾行代碼,那么他就能很好地了解程序正在做什么以及錯誤發生在什么地方。

(2)設置斷點
通過設置斷點可以使程序在執行到某一點時暫時停住。當你知道錯誤發生在程序的哪一部分時,這種方法是特別有用的。你可以把斷點設置在有問題的程序段的前面、中間或后面。當程序執行到斷點時,就會暫時停住,此時你可以檢查所有局部變量、參數和全局變量的值。如果一切正常,可以繼續執行程序,直到遇到另一個斷點,或者直到引起問題的原因暴露出來。

(3)設置監視
程序員可以通過調試程序監視一個變量,即連續地監視一個變量的值或內容。如果你清楚一個變量的取值范圍或有效內容,那么通過這種方法就能很快地找出錯誤的原因。此外,你可以讓調試程序替你監視變量,并且在某個變量超出預先定義的取值范圍或某個條件滿足時使程序暫停執行。如果你知道變量的所有行為,那么這么做是很方便的。

好的調試程序通常還提供一些其它功能來簡化調試工作。然而,調試程序并不是唯一的調試工具,lint程序和編譯程序本身也能提供很有價值的手段來分析程序的運行情況。

注意:lint程序能分辨數百種常見的編程錯誤,并且能報告這些錯誤發生在程序的哪一部分。盡管其中有一些并不是真正的錯誤,但大部分還是有價值的。

lint程序和編譯程序所提供的一種典型功能是編譯時檢查(compile—time checks),這種功能是調試程序所不具備的。當用這些工具編譯你的程序時,它們會找出程序中有問題的程序段,可能產生意想不到的效果的程序段,以及常見的錯誤。下面將分析幾個這種檢查方式的應用例子,相信對你會有所幫助。

等于運算符的誤用

編譯時檢查有助于發現等于運算符的誤用。請看下述程序段:
??? void foo(int a,int b)
??? {
????? if ( a = b )
????? {
????????? / * some code here * /
????? }
??? }
這種類型的錯誤一般很難發現!程序并沒有比較兩個變量,而是把b的值賦給了a,并且在b不為零的條件下執行if體。一般來說,這并不是程序員所希望的(盡管有可能)。這樣一來,不僅有關的程序段將被執行錯誤的次數,并且在以后用到變量a時其值也是錯誤的。

未初始化的變量

編譯時檢查有助于發現未初始化的變量。請看下面的函數:
void average ( float ar[], int size )
{
?????? float total;
?????? int a;
?????? for( a = 0;a<size; ++a)
?????? {
??????????? total+=ar[a];
?????? }
?????? printf(" %f\n", total /? (float) size );
}
這里的問題是變量total沒有被初始化,因此它很可能是一個隨機的無用的數。數組所有元素的值的和將與這個隨機數的值相加(這部分程序是正確的),然后輸出包括這個隨機數在內的一組數的平均值。

變量的隱式類型轉換

在有些情況下,C語言會自動將一種類型的變量轉換為另一種類型。這可能是一件好事(程序員不用再做這項工作),但是也可能會產生意想不到的效果。把指針類型隱式轉換成整型恐怕是最糟糕的隱式類型轉換。
void sort( int ar[],int size )
{
?????? /* code to sort goes here * /
}
int main()
{
?????? int arrgy[10];
?????? sort(? 10, array );
}
上述程序顯然不是程序員所期望的,雖然它的實際運行結果難以預測,但無疑是災難性的。

用什么辦法才能找出程序中的錯誤?

在調試程序的過程中,程序員應該記住以下幾種技巧:
先調試程序中較小的組成部分,然后調試較大的組成部分
如果你的程序編寫得很好,那么它將包含一些較小的組成部分,最好先證實程序的這些部分是正確的。盡管程序中的錯誤并不一定發生在這些部分中,但是先調試它們有助于你理解程序的總體結構,并且證實程序的哪些部分不存在錯誤。進一步地,當你調試程序中較大的組成部分時,你就可以確信那些較小的組成部分是正常工作的。

徹底調試好程序的一個組成部分后,再調試下一個組成部分
這一點非常重要。如果證實了程序的一個組成部分是正確的,不僅能縮小可能存在錯誤的范圍,而且程序的其它組成部分就能安全地使用這部分程序了。這里應用了一種很好的經驗性原則,簡單地說就是調試一段代碼的難度與這段代碼長度的平方成正比,因此,調試一段20行的代碼比調試一段10行的代碼要難4倍。因此,在調試過程中每次只把精力集中在一小段代碼上是很有幫助的。當然,這僅僅是一個總的原則,具體使用時還要視具體情況而定。

連續地觀察程序流(flow)和數據的變化
這一點也很重要!如果你小心仔細地設計和編寫程序,那么通過監視程序的輸出你就能準確地知道正在執行的是哪部分代碼以及各個變量的內容都是什么。當然,如果程序表現不正常,你就無法做到這一點。為了做到這一點,通常只能借助于調試程序或者在程序中加入大量的print語句來觀察控制流和重要變量的內容。

始終打開編譯程序警告選項? 并試圖消除所有警告
在開發程序的過程中,你自始至終都要做到這一點,否則,你就會面臨一項十分繁重的工作。盡管許多程序員認為消除編譯程序警告是一項繁瑣的工作,但它是很有價值的。編譯程序給出警告的大部分代碼至少都是有問題的,因此用一些時間把它們變成正確的代碼是值得的;而且,通過消除這些警告,你往往會找到程序中真正發生錯誤的地方。

準確地縮小存在錯誤的范圍
如果你能一下子確定存在錯誤的那部分程序并在其中找到錯誤,那就會節省許多調試時間,并且你能成為一個收入相當高的專業調試員。但事實上,我們并不能總是一下子就命中要害,因此,通常的做法是逐步縮小可能存在錯誤的程序范圍,并通過這種過程找出真正存在錯誤的那部分程序。不管錯誤是多么難于發現,這種做法總是有效的。當你找到這部分程序后,就可以把所有的調試工作集中到這部分程序上了。不言而喻,準確地縮小范圍是很重要的,否則,最終集中精力調試的那部分程序很可能是完全正確的。

如何從一開始就避免錯誤?

有這樣一句諺語——“防患于未然”,它的意思是避免問題的出現比出現問題后再想辦法彌補要好得多。這在計算機編程中也是千真萬確的!在編寫程序時,一個經驗豐富的程序員所花的時間和精力要比一個缺乏經驗的程序員多得,但正是這種耐心和嚴謹的編程風格使經驗豐富的程序員往往只需花很少的時間來調試程序,而且,如果此后程序要解決某個問題或做某種改動,他便能很快地修正錯誤并加入相應的代碼。相反,對于一個粗制濫造的程序,即使它總的來說還算正確,那么改動它或者修正其中一個很快就暴露出來的錯誤,都會是一場惡夢。

一般來說,按結構化程序設計原則編寫的程序是易于調試和修改的,下面將介紹其中的一些原則。

程序中應有足夠的注釋
有些程序員認為注釋程序是一項繁瑣的工作,但即使你從來沒想過讓別人來讀你的程序,你也應該在程序中加入足夠的注釋,因為即使你現在認為清楚明了的語句,在幾個月以后往往也會變得晦澀難懂。這并不是說注釋越多越好,過多的注釋有時反而會混淆代碼的原意。但是,在每個函數中以及在執行重要功能或并非一目了然的代碼前加上幾行注釋是必要的。下面就是一段注釋得較好的代碼:
? /*??
?? *?? Compute an integer factorial value using recursion.
?? *?? Input?? an integer number.
?? *?? Output? : another integer
?? *?? Side effects : may blow up stack if input value is? * Huge *
?? */
int factorial ( int number)
{
?????? if ( number < = 1)
?????????? return 1;? /* The factorial of one is one; QED * /
?????? else
??????????? return n * factorial( n - 1 );
?????? / * The magic! This is possible because the factorial of a
???????? number is the number itself times the factorial? of the
???????? number minus one.? Neat!? * /
}
函數應當簡潔
按照前文中曾提到的這樣一條原則——調試一段代碼的難度和這段代碼長度的平方成正比——函數編寫得簡潔無疑是有益的。但是,需要補充的是,如果一個函數很簡潔,你就應該多花一點時間去仔細地分析和檢查它,以確保它準確無誤。此后你可以繼續編寫程序的其余部分,并且可以對剛才編寫的函數的正確性充滿信心,你再也不需要檢查它了。對于一段又長又復雜的例程,你往往是不會有這樣的信心的。

編寫短小簡潔的函數的另一個好處是,在編寫了一個短小的函數之后,在程序的其它部分就可以使用這個函數了。例如,如果你在編寫一個財務處理程序,那么你在程序的不同部分可能都需要按季、按月、按周或者按一月中的某一天等方式來計算利息。如果按非結構化原則編寫程序,那么在計算利息的每一處都需要一段獨立的代碼,這些重復的代碼將使程序變得冗長而難讀。然而,你可以把這些任務的實現簡化為下面這樣的一個函數:
??/*
?? *??? ComDllte what the "real" rate of interest would be
?? *???? for a given flat interest rate, divided into N segments
?? */
double Compute Interest( double Rate, int Segments )
{
?????? int? a;
?????? double Result = 1.0;
?????? Rate /= (double) Segments;
?????? for( a = 0; a< Segments? ; ++a )
?????????? Result * =Rate;
?????? return Result;
}
在編寫了上述函數之后,你就可以在計算利息的每一處調用這個函數了。這樣一來,你不僅能有效地消除每一段復制的代碼中的錯誤,而且大大縮短了程序的長度,簡化了程序的結構。這種技術往往還會使程序中的其它錯誤更容易被發現。

當你習慣了用這種方法把程序分解為可控制的模塊后,你就會發現它還有更多的妙用。

程序流應該清晰,避免使用goto語句和其它跳轉語句
這條原則在計算機技術領域內已被廣泛接受,但在某些圈子中對此還很有爭議。然而,人們也一致認為那些通過少數語句使程序流無條件地跳過部分代碼的程序調試起來要容易得多,因為這樣的程序通常更加清晰易懂。許多程序員不知道如何用結構化的程序結構來代替那些“非結構化的跳轉”,下面的一些例子說明了應該如何完成這項工作:
for( a = 0; a<100s ++a)
{
???? Func1( a );
???? if (a? = = 2 ) continue;
???? Func2( a );
}

當a等于2時,這段程序就通過continue語句跳過循環中的某余部分。它可以被改寫成如下的形式:
for( a = 0; a<100; ++a)
{
???? Func1 (a);
???? if (a !=2 )
??????? Func2(a) ;
}
這段程序更易于調試,因為花括號內的代碼清楚地顯示了應該執行和不應該執行什么。那么,它是怎樣使你的代碼更易于修改和調試的呢?假設現在要加入一些在每次循環的最后都要被執行的代碼,在第一個例子中,如果你注意到了continue語句,你就不得不對這段程序做復雜的修改(不妨試一下,因為這并非是顯而易見的!);如果你沒有注意到continue語句,那么你恐怕就要犯一個難以發現的錯誤了。在第二個例子中,要做的修改很簡單,你只需把新的代碼加到循環體的末尾。

當你使用break語句時,可能會發生另外一種錯誤。假設你編寫了下面這樣一段程序:
for (a =0) a<100;? ++a)
{
???? if (Func1 (a) ==2 )
??????? break;
???? Func2 (a)? ;
}
假設函數Funcl()的返回值永遠不會等于2,上述循環就會從1進行到100;反之,循環在到達100以前就會結束。如果你要在循環體中加入代碼,看到這樣的循環體,你很可能就會認為它確實能從0循環到99,而這種假設很可能會使你犯一個危險的錯誤。另一種危險可能來自對a值的使用,因為當循環結束后,a的值并不一定就是100。

c語言能幫助你解決這樣的問題,你可以按如下形式編寫這個for循環:
??? for(a=O;a<100&&Func1(a)!=2;++a)
上述循環清楚地告訴程序員:“從0循環到99,但一旦Func1()等于2就停止循環”。因為整個退出條件非常清楚,所以程序員此后就很難犯前面提到的那些錯誤了。

函數名和變量名應具有描述性
使用具有描述性的函數和變量名能更清楚地表達代碼的意思——并且在某種程度上這本身就是一種注釋。以下幾個例子就是最好的說明:
??? y=p+i-c;

??? YearlySum=Principal+Interest-Charges:
哪一個更清楚呢?
??? p=*(l+o);

??? page=&List[offset];
哪一個更清楚呢?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450580.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450580.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450580.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何賦值hook定義的變量

1、定義變量 const { tableProps, mutate} useRequest(async (params {}) > {const { success, data, total } await Api.getUserAccountApi({page: params.current || 1,...searchValue,});return {list: success ? data : [],total: success ? total : 0,};},{pagin…

java中的sleep()和wait()的區別

對于sleep()方法&#xff0c;我們首先要知道該方法是屬于Thread類中的。而wait()方法&#xff0c;則是屬于Object類中的。sleep()方法導致了程序暫停執行指定的時間&#xff0c;讓出cpu該其他線程&#xff0c;但是他的監控狀態依然保持者&#xff0c;當指定的時間到了又會自動恢…

Webpack4干貨分享(二),使用loader處理scss,圖片以及轉換JS

轉載請注明出處&#xff1a; 葡萄城官網 &#xff0c;葡萄城為開發者提供專業的開發工具、解決方案和服務&#xff0c;賦能開發者。 原文出處&#xff1a; https://wanago.io/2018/07/16/webpack-4-course-part-two-webpack-4-course-part-two-loaders/今天繼續我們的Webpack 4…

spring-data-jpa 介紹 復雜查詢,包括多表關聯,分頁,排序

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本篇進行Spring-data-jpa的介紹&#xff0c;幾乎涵蓋該框架的所有方面&#xff0c;在日常的開發當中&#xff0c;基本上能滿足所有需求。…

重寫laravel的request的校驗器

protected function failedValidation(Validator $validator) {$error $validator->errors()->all();throw new HttpResponseException(response()->json([data>[],code>500,msg>$error[0]], 500)); }

如何檢測C語言中的內存漏洞(leak)?

在動態分配的內存單元(即由函數malloc()或ealloc()分配的內存單元)不再使用卻沒有被釋放的情況下&#xff0c;會出現內存漏洞。未釋放內存單元本身并不是一種錯誤&#xff0c;編譯程序不會因此報告出錯&#xff0c;程序也不會因此而立即崩潰。但是&#xff0c;如果不再使用而又…

Oracle中row_number()、rank()、dense_rank() 的區別

link:https://www.cnblogs.com/qiuting/p/7880500.html轉載于:https://www.cnblogs.com/Spring-Rain/p/9716213.html

Vim使用教程(按鍵教程,映射都可以改的,持續更新)

修改映射 tnvim .vimrc 1、spaceft 調出目錄 2、controlh 跳到目錄 3、controll 跳到內容頁 4、spacekn 運行最近一個測試 5、spacekf 運行整個測試文件 6、spacekl 運行最后一個測試 7、sv 分屏 8、sq 退出分屏 9、controlp 搜索功能 10、/搜索內容 N向上…

Alpha 沖刺五

團隊成員 051601135 岳冠宇051604103 陳思孝031602629 劉意晗031602248 鄭智文031602234 王淇會議照片 項目燃盡圖 項目進展 暫無實質性進展。 項目描述 問題困擾&#xff1a; 商品分類出現困惑。交互部分向服務器發送請求失敗&#xff0c;安卓在4.0后對網絡請求有限制要求&…

什么是換碼符(escape character)?

換碼符是用來執行一個命令或一項任務的字符&#xff0c;它們不會被打印到屏幕上。例如&#xff0c;一個換碼符可以是這樣一個字符&#xff0c;它被傳遞給一個設備&#xff0c;告訴計算機屏幕以紅色而不是通常的白色來顯示下一行。這個換碼符將和真正要被設備以紅色來顯示的字符…

java日期工具類DateUtil

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 DateUtil類 [java] view plain copy package com.util; import java.text.SimpleDateFormat; import java.util.ArrayList; import…

Command 傳參的幾種方式

Command可以根據CommandParameter傳參 關鍵代碼 public ICommand SubmitCommand > _submitCommand; private RelayCommand _submitCommand new RelayCommand(new Action<object>(ShowMessage)); private static void ShowMessage(object obj) {MessageBox.Show(obj.T…

phpStorm重構快捷鍵(mac系統、持續更新)

參考 https://learnku.com/laravel/t/5420/your-keyboard-shortcuts-please 1、ctrlaltf 將表達式提取出來使其成為類的屬性&#xff0c;并自動更新引用。 2、ctrlaltp 將表達式變成由參數傳入 3、crtlF6 修改函數簽名&#xff08;函數名&#xff0c;函數參數&#xff09…

python -m xxx.py和python xxx.py的區別

先看下python -m site作用是顯示sys.path的值內容&#xff0c;也就是python搜索模塊的目錄&#xff0c;作用類似于linux下的PATH python -m SimpleHTTPServer 會在sys.path的所有路徑下查找SimpleHTTPServer.py文件&#xff1b; 而python SimpleHTTPServer.py則是在當前查找文…

C語言多維數組與多級指針

多維數組與多級指針也是初學者感覺迷糊的一個地方。超過二維的數組和超過二級的指針其實并不多用。如果能弄明白二維數組與二級指針&#xff0c;那二維以上的也不是什么問題了。所以本節重點討論二維數組與二級指針。一、二維數組 1、假想中的二維數組布局我們前面討論過&…

spring-data-jpa 使用

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 只是記錄下使用方法 &#xff1a; 1. RPC接口 service實現類調用&#xff0c;PageRequest對象會實現分頁、排序。 Overridepublic Obj…

docker的安裝與安裝mysql(mac,centos為例)

一、mac安裝docker 1、去這個網站下載安裝https://www.docker.com/get-started 2、運行docker 運行開啟docker docker run -dp 80:80 docker/getting-started 二、centos8安裝docker 1、安裝docker 下載低版本的 yum install -y docker-ce --nobest 2、安裝擴展 wget http…

配置PPPOE

先配置服務端PPPOE-Server先為路由添加一個賬號為PPP所使用[PPPOE-Server]aaa [PPPOE-Server-aaa]local-user test password cipher 123 //添加一個本地賬號[PPPOE-Server-aaa]local-user test service-type ppp//設置test賬號類型為PPP賬號[PPPOE-Server]ip pool test//添加一…

eclipse棄坑記第一篇之在idea上配置Tomcat環境并創建Javaweb項目的詳細步驟原創

IntelliJ IDEA是一款功能強大的開發工具&#xff0c;在代碼自動提示、重構、J2EE支持、各類版本工具(如git、svn、github)、maven等方面都有很好的應用。 IntelliJ IDEA有免費的社區版和付費的旗艦版。免費版只支持Java等為數不多的語言和基本的IDE特性&#xff0c;旗艦版還支持…

laravel安裝prettier,git hook代碼格式化工具

1、安裝prettier的php擴展 npm install --global prettier prettier/plugin-php 2、安裝husky&#xff0c;lint-staged&#xff08;git鉤子&#xff09;使用 npm i prettier lint-staged husky -D 3、修改package.json文件 在scripts后面添加兩個函數 "husky": …