• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            程序讓生活更美好

            半畝方塘 天光云影

              C++博客 ::  :: 新隨筆 :: 聯(lián)系 ::  :: 管理 ::
              55 隨筆 :: 4 文章 :: 202 評(píng)論 :: 0 Trackbacks

            編寫易于調(diào)試的VC代碼(轉(zhuǎn)載)
            提供者:bluejoe 張貼時(shí)間:2007-01-23 17:00:05.0 出處:csdn.net 作者:michael 

            一 程序的設(shè)計(jì)

              要避免錯(cuò)誤,首先要從好的設(shè)計(jì)開(kāi)始。對(duì)于程序的設(shè)計(jì),需考慮到程序的兩個(gè)特性:

              1簡(jiǎn)單性

              大多數(shù)常見(jiàn)的錯(cuò)誤來(lái)源于程序設(shè)計(jì)中不必要的復(fù)雜成分。一個(gè)好的設(shè)計(jì)應(yīng)該反映問(wèn)題本身的要求,而不必為了刻意追求“滿足將來(lái)的需要”而添加不必要的特性。實(shí)際上,簡(jiǎn)單優(yōu)雅的設(shè)計(jì)比那些復(fù)雜的設(shè)計(jì)更能迎合未來(lái)的需求。

              2 耦合性

              耦合(decoupling)性用來(lái)衡量不同對(duì)象之間的依賴程度。松耦合的程序易于理解和實(shí)現(xiàn),易于測(cè)試和維護(hù),且這種程序包含錯(cuò)誤的可能性小,錯(cuò)誤也較容易發(fā)現(xiàn)和清除。
             

            二 編程風(fēng)格

              編程風(fēng)格是個(gè)人問(wèn)題,有很大的隨意性。一個(gè)好的編程風(fēng)格不僅讓代碼易理解,也易于調(diào)試。好的編程風(fēng)格包括:

              1 清晰地書寫代碼

              如果沒(méi)有必要,盡量不要使用語(yǔ)言中的高級(jí)特性,因?yàn)檫@些特性不易于理解和調(diào)試。使用大多數(shù)程序員都能理解的語(yǔ)言成分來(lái)書寫代碼不易犯錯(cuò)且易于理解和維護(hù)。

              2 編寫結(jié)構(gòu)良好的代碼

              當(dāng)程序崩潰時(shí)所能得到的最基本的調(diào)試信息是源代碼文件、問(wèn)題所在行的行號(hào)和一個(gè)調(diào)用棧(call stack)。調(diào)用棧是調(diào)試程序時(shí)最有幫助的部分,它提供錯(cuò)誤出現(xiàn)的上下文,也就是帶參數(shù)的函數(shù)調(diào)用序列。你書寫的代碼結(jié)構(gòu)越好,調(diào)用棧就能給你越多信息。

              3 使用良好的標(biāo)識(shí)符

               一個(gè)好名字能使你的代碼更容易被理解和維護(hù)。流行的匈牙利命名法(Hungarian Notation)實(shí)際上是把標(biāo)識(shí)符的意義和表示方法結(jié)合起來(lái)。 現(xiàn)在,匈牙利命名法表現(xiàn)出不少的局限性,匈牙利命名法過(guò)于看重前綴的作用,對(duì)一個(gè)變量的表達(dá)信息不完整,實(shí)際上并沒(méi)有傳遞多少有用信息,它使代碼難于閱 讀,難以維護(hù)。一個(gè)好的命名傳統(tǒng)是指示出變量的作用域以便在需要的時(shí)候檢查它的定義,并明確地指出一個(gè)變量是全局的、局部的還是成員數(shù)據(jù)。依賴變量的定義 比依賴匈牙利前綴更加有用和可靠。

              好的名字能夠用平常的語(yǔ)言概括出該標(biāo)識(shí)符所代表的實(shí)體的含義。在選擇類、函數(shù)、變量的名字時(shí)可以考慮以下幾個(gè)原則:

                取簡(jiǎn)單的描述性名字,好的名字能簡(jiǎn)要地概括出這個(gè)標(biāo)識(shí)符代表的含義。

                避免簡(jiǎn)寫,簡(jiǎn)寫使標(biāo)識(shí)符難于閱讀和記憶,盡量使用混合大小寫的完整的單詞。

                避免相似性的文字,避免混淆。

                避免采用一般的或隨機(jī)產(chǎn)生的名字,而應(yīng)采用有實(shí)際意義的名字。如欲從按鈕類派生位圖按鈕,取一個(gè)CBitmapButton,而不是CMyButton。

              4 用簡(jiǎn)單的語(yǔ)句行

              在VC中,一行可寫多個(gè)語(yǔ)句。但調(diào)試是面向行的,過(guò)于復(fù)雜的行難于調(diào)試。因此,從調(diào)試的角度出發(fā),每一個(gè)語(yǔ)句都應(yīng)獨(dú)自成行。

              5 使用統(tǒng)一的排列

              統(tǒng)一的排列方式使類、變量的定義和語(yǔ)句更加明顯。

              6 用括號(hào)使書寫清晰

              你不一定能都記住各種運(yùn)算符的優(yōu)先級(jí)和結(jié)合律,而使用多余的括號(hào)并不影響編譯后的代碼。因此,如果你不能確定是否需要括號(hào)時(shí),請(qǐng)加上它。

              7 使用好的注釋

              用好的注釋能使你的代碼不易出錯(cuò),而且便于其他程序員閱讀,便于理解和維護(hù)。

            三 編寫程序時(shí)應(yīng)注意的問(wèn)題

              1 充分利用VC++的特性

              可用下列技術(shù)來(lái)充分利用VC++的編譯器的特性:

             ?。?)用const代替#define來(lái)創(chuàng)建常量;

             ?。?)用enum代替#define來(lái)創(chuàng)建常量集合;

             ?。?)用內(nèi)聯(lián)(inline)函數(shù)代替#define;

               這三種技術(shù)用C++而不是C預(yù)處理。使用預(yù)處理的問(wèn)題在于編譯器對(duì)于預(yù)處理器所作的事情一無(wú)所知,因此無(wú)法用數(shù)據(jù)類型檢查錯(cuò)誤和不一致的地方。預(yù)處理的 名字不在符號(hào)表里,因此也不能用調(diào)試工具來(lái)檢查預(yù)處理常量。相似地,預(yù)處理宏被編譯進(jìn)去,不能用調(diào)試工具跟蹤。編譯器能充分了解const、enum和 inline語(yǔ)句,從而能在編譯的時(shí)候?qū)Τ霈F(xiàn)的問(wèn)題發(fā)出警告。

              但預(yù)處理在很多調(diào)試代碼中起重要作用。調(diào)試代碼經(jīng)常需要從非調(diào)試代碼里面得到不同的行為,而最有效的辦法就是讓預(yù)處理為調(diào)試創(chuàng)建不同的代碼。

             ?。?)用new和delete代替malloc和free;

              在創(chuàng)建對(duì)象、類型的安全性和靈活性方面。使用new/delete比malloc/free要好。另外,new可被重載,提供了更大的靈活性。

             ?。?)用輸入輸出流(iostreams)代替stdio。

               使用C++輸入輸出流(<<和>>)而不使用C標(biāo)準(zhǔn)輸入輸出庫(kù)(printf/sprintf和scanf/sscanf), 有利于安全性和擴(kuò)展性。從調(diào)試的角度來(lái)看,標(biāo)準(zhǔn)輸入輸出函數(shù)的最大問(wèn)題在于編譯器不能對(duì)控制流參數(shù)進(jìn)行任何類型檢測(cè),而輸入輸出流的任何問(wèn)題都能在編譯時(shí) 檢測(cè)出來(lái)。

              2 使用頭文件

              要在頭文件中聲明所有共享的外部符號(hào),而且保留函數(shù)原型中的參數(shù)名。把所有的共享定義放在頭文件中,不要在.cpp文件里面看到extern關(guān)鍵字。

              3 初始化變量

               在使用變量之前一定要把它們初始化。在初始化之前就使用變量肯定會(huì)產(chǎn)生錯(cuò)誤。通常不需對(duì)對(duì)象進(jìn)行初始化,對(duì)對(duì)數(shù)據(jù)成員應(yīng)在構(gòu)造函數(shù)中初始化。必須明確地 為在棧中和堆中分配的數(shù)組和數(shù)據(jù)結(jié)構(gòu)進(jìn)行初始化。對(duì)于對(duì)象,應(yīng)該初始化每個(gè)需要初始化的數(shù)據(jù)成員。因?yàn)樽兞康氖褂檬怯蓛?yōu)化器來(lái)檢查的,所以檢測(cè)未初始化的 本地變量,發(fā)布版本要比調(diào)試版本要做得好。

              4 使用布爾表達(dá)式

            C++的布爾類型:bool,值為true和false,大小為一個(gè)字節(jié)。

            Windows程序通常用BOOL類型。定義如下:

            1Typedef int BOOL;    
            2  
            3#define FALSE 0    
            4  
            5#define TRUE 1   

            在C++中,一個(gè)布爾表達(dá)式如果為0則為假,其他則為真。因此,對(duì)布爾表達(dá)式應(yīng)該檢查是否問(wèn)假而不是檢查是否為真。

              5 使用句柄和指針

              初始化一個(gè)指針時(shí),要么讓其指向一個(gè)有效的內(nèi)存地址,要么設(shè)為0(空指針),避免指針指向無(wú)效地址?;厥罩羔?biāo)笇?duì)象時(shí)要重新初始化這個(gè)指針,并且在指針被釋放前為空時(shí)就對(duì)其進(jìn)行處理。對(duì)句柄的處理跟指針一樣。

              6 用引用而不是指針做參數(shù)

              用指針做函數(shù)的參數(shù)可傳遞一個(gè)空指針,很靈活,但也很容易忘了對(duì)指針進(jìn)行初始化。而引用是對(duì)象的別名,它必須和有效的對(duì)象相關(guān)聯(lián),不存在空的和沒(méi)有初始化的引用。當(dāng)在函數(shù)中收到一個(gè)引用參數(shù)時(shí),可以肯定這是一個(gè)有效的對(duì)象。程序用引用做參數(shù)比用指針做參數(shù)更為健壯。

              7 強(qiáng)制類型轉(zhuǎn)換(cast)

               進(jìn)行數(shù)據(jù)類型的強(qiáng)制類型轉(zhuǎn)換時(shí),將會(huì)調(diào)用相應(yīng)的構(gòu)造函數(shù)或轉(zhuǎn)換函數(shù)來(lái)創(chuàng)建一個(gè)新類型的臨時(shí)對(duì)象。對(duì)指針的正確類型轉(zhuǎn)換可消除一個(gè)編譯錯(cuò)誤,但并沒(méi)改變指 針。強(qiáng)制類型轉(zhuǎn)換破壞了編譯器進(jìn)行類型檢查的功能,而這正是編譯器查找錯(cuò)誤的最有效的機(jī)制。為了保證安全性,每一個(gè)強(qiáng)制類型轉(zhuǎn)換都需要手工進(jìn)行類型檢查。 為盡量避免強(qiáng)制類型轉(zhuǎn)換,你可以:避免使用多態(tài)數(shù)據(jù)類型;使用更加廣泛的基類;提供特殊的存取函數(shù);讓編譯器隱式處理類型轉(zhuǎn)換等措施。

              8 使用構(gòu)造函數(shù)和析構(gòu)函數(shù)

               構(gòu)造函數(shù)需要分配內(nèi)存,創(chuàng)建資源或者打開(kāi)文件,這些運(yùn)算并不總是成功。構(gòu)造函數(shù)沒(méi)有返回值,沒(méi)有直接顯示錯(cuò)誤的方法。一個(gè)常見(jiàn)的方法(在很多MFC類中 使用)是把對(duì)象創(chuàng)建分為兩步:第一步,讓構(gòu)造函數(shù)以一種不會(huì)出錯(cuò)的方式初始化對(duì)象;第二步,讓某些初始化函數(shù)(如Init或Open)完成工作,這一步可 能出錯(cuò)。另一種方法是在構(gòu)造函數(shù)中使用異常:第一步,以不會(huì)出錯(cuò)的方式初始化對(duì)象;第二步,用可能在try段內(nèi)出錯(cuò)的代碼初始化對(duì)象;第三步,在 catch代碼里面處理異常。如果出現(xiàn)異常,就會(huì)在構(gòu)造函數(shù)里清除分配的資源,并且再次拋出異常。

              異常處理的一個(gè)關(guān)鍵細(xì)節(jié)就是在棧展 開(kāi)的過(guò)程中拋出的異常會(huì)終止整個(gè)應(yīng)用程序。在處理異常時(shí)經(jīng)常要調(diào)用析構(gòu)函數(shù),因此析構(gòu)函數(shù)很容易出錯(cuò),一定要保證析構(gòu)函數(shù)的異常在析構(gòu)函數(shù)中得到處理。要 保證基類的析構(gòu)函數(shù)是虛函數(shù)。這樣,就算對(duì)象是一個(gè)指向基類的指針,也會(huì)調(diào)用派生類的析構(gòu)函數(shù)。否則,就會(huì)引起資源泄漏(resource leak)。  

            在VC程序中使用調(diào)試語(yǔ)句

            為了更好地對(duì)程序調(diào)試,可以使用如下方法:使用斷言、使用跟蹤語(yǔ)句、使用異常和返回值。

            一、斷言

            1、基本概念

              斷言是一種讓錯(cuò)誤在運(yùn)行時(shí)候自我暴露的簡(jiǎn)單有效實(shí)用的技術(shù)。它們幫助你較早較輕易地發(fā)現(xiàn)錯(cuò)誤,使得整個(gè)調(diào)試過(guò)程效率更高。

              斷言是布爾調(diào)試語(yǔ)句,用來(lái)檢測(cè)在程序正常運(yùn)行的時(shí)候某一個(gè)條件的值是否總為真,它能讓錯(cuò)誤在運(yùn)行時(shí)刻暴露在程序員面前。使用斷言的最大好處在于,能在更解決錯(cuò)誤的發(fā)源地的地方發(fā)現(xiàn)錯(cuò)誤。斷言具有以下特征:

            .斷言是用來(lái)發(fā)現(xiàn)運(yùn)行時(shí)刻錯(cuò)誤的,發(fā)現(xiàn)的錯(cuò)誤是關(guān)于程序?qū)崿F(xiàn)方面的。

            .斷言中的布爾表達(dá)式顯示的是某個(gè)對(duì)象或者狀態(tài)的有效性而不是正確性。

            .斷言在條件編譯后只存在于調(diào)試版本中,而不是發(fā)布版本里。

            .斷言不能包含程序代碼。

            .斷言是為了給程序員而不是用戶提供信息。

               使用斷言最根本的好處是自動(dòng)發(fā)現(xiàn)許多運(yùn)行時(shí)產(chǎn)生的錯(cuò)誤,但斷言不能發(fā)現(xiàn)所有錯(cuò)誤。斷言檢查的是程序的有效性而不是正確性,可通過(guò)斷言把錯(cuò)誤限制在一個(gè)有 限的范圍內(nèi)。當(dāng)斷言為假,激活調(diào)試器顯示出錯(cuò)代碼時(shí),可用Call Stack命令,通過(guò)檢查棧里的調(diào)用上下文、少量相關(guān)參數(shù)的值以及輸出窗口中 Debug表的內(nèi)容,通常能檢查出導(dǎo)致斷言失敗的原因。_ASSERTE宏(屬于C運(yùn)行時(shí)間庫(kù))還能在斷言失敗時(shí)顯示出失效斷言。下面我們討論一下MFC 庫(kù)中的斷言。

            2、MFC庫(kù)中的斷言

            (1) ASSERT(布爾表達(dá)式)

            用MFC時(shí)最好選擇ASSERT宏,它的優(yōu)點(diǎn)是即使出現(xiàn)了WM_QUIT消息也能顯示斷言失效消息框。

            (2) VERIFY(布爾表達(dá)式)

            VERIFY 宏中的布爾表達(dá)式在發(fā)布版本中被保留下來(lái)。VERIFY宏簡(jiǎn)化了對(duì)函數(shù)返回值的檢查,一般用來(lái)檢查Windows API的返回值。由于 VERIFY宏里的布爾表達(dá)式在發(fā)布版本里保留了下來(lái),因此最好盡量不要使用這個(gè)宏以實(shí)現(xiàn)程序代碼和調(diào)試代碼的完全分離。

            (3 )ASSERT_VALID(指向CObject派生類對(duì)象的指針)

            ASSERT_VALID宏通過(guò)調(diào)用重載的AssertValid函數(shù)來(lái)確定指向CObject派生類對(duì)象的指針是否有效。無(wú)論你什么時(shí)候從CObject派生類中得到一個(gè)對(duì)象,在對(duì)這個(gè)對(duì)象做任何操作之前都應(yīng)該調(diào)用ASSERT_VALID宏。

            (4) ASSERT_KINDOF(類名, 指向CObject派生類對(duì)象的指針)

            這個(gè)宏用來(lái)驗(yàn)證指向CObject派生類對(duì)象的指針是否從某個(gè)特殊類中派生,在調(diào)用它之前先調(diào)用ASSERT_VALID宏。只有在很特殊的場(chǎng)合下才用得到,如檢測(cè)編譯器可能錯(cuò)過(guò)的對(duì)象類型問(wèn)題。

            此外,還有兩個(gè)沒(méi)有正式文件的ASSERT宏的變種:ASSERT_POINTER(指針,指針類型),ASSERT_NULL_OR_POINTER(指針,指針類型)。

            3、什么時(shí)候使用斷言

              把斷言看作一種簡(jiǎn)單的制造柵欄的方法,這種柵欄能使錯(cuò)誤在穿過(guò)自己時(shí)暴露。

            .檢查函數(shù)的輸入

            .檢查函數(shù)的輸出

            .檢查對(duì)象的當(dāng)前狀態(tài)

            .堅(jiān)持邏輯變量的合理性和一致性

            .檢查類中的不變量

            公有成員函數(shù)比私有和保護(hù)的成員函數(shù)需要更全面的斷言。

              不正確地使用斷言會(huì)導(dǎo)致錯(cuò)誤。斷言應(yīng)該檢測(cè)那些在程序正常運(yùn)行的時(shí)候永遠(yuǎn)都不可能出現(xiàn)的狀態(tài)。斷言是用來(lái)揭示錯(cuò)誤的,而不是用來(lái)糾正運(yùn)行時(shí)刻錯(cuò)誤的。

            4、斷言與防御性編程(Defensive Programming)

               斷言在調(diào)試的時(shí)候向程序員揭示運(yùn)行時(shí)刻錯(cuò)誤(調(diào)試版本里),而防御性編程使用戶在運(yùn)行程序(發(fā)布版本里)時(shí),當(dāng)出現(xiàn)意外情況時(shí)程序仍能繼續(xù)工作。實(shí)際 上,防御性的編程要求程序在檢測(cè)到意外時(shí)返回一個(gè)“安全”的值(比如布爾函數(shù)返回false,指針和句柄返回空值),一個(gè)錯(cuò)誤代碼或者拋出一個(gè)異常來(lái)解決 問(wèn)題。特定的防御性編程技術(shù)包括:處理無(wú)效函數(shù)參數(shù)和數(shù)據(jù)、出現(xiàn)問(wèn)題的時(shí)候程序失敗、檢查臨界函數(shù)返回的錯(cuò)誤代碼以及處理異常。需要防御性編程的標(biāo)準(zhǔn)問(wèn)題 包括:錯(cuò)誤的輸入數(shù)據(jù)、內(nèi)存或者硬盤空間不夠、不能打開(kāi)一個(gè)文件、外部設(shè)備不能訪問(wèn)、網(wǎng)絡(luò)連接不上或者甚至在程序中還有錯(cuò)誤,目的是保持程序的運(yùn)行狀態(tài)。 如果你的程序是防御性的,別忘了使用斷言。如果你使用斷言,也別忘了防御性編程。這兩種技術(shù)最好結(jié)合在一起使用。

            二、跟蹤語(yǔ)句

            1、基本概念

               跟蹤語(yǔ)句(trace statements)可使程序執(zhí)行,并使程序員可對(duì)可變值進(jìn)行查看。它們提供了一個(gè)用于觀察的程序,并且獨(dú)立于一個(gè)交互式的調(diào) 試器,但是最具有特色的是它們常用于對(duì)調(diào)試器提供的信息進(jìn)行補(bǔ)充。在VC中,跟蹤消息通常輸出到輸出窗口中的Debug標(biāo)簽,也可以重新輸出到一個(gè)文件 中。跟蹤語(yǔ)句的特性如下:

            .跟蹤語(yǔ)句用于報(bào)告代碼中重要的運(yùn)行事件。

            .跟蹤語(yǔ)句的編譯通常是有條件的,并只存在于調(diào)試版本中,而在發(fā)布版本中不被編譯。

            .跟蹤語(yǔ)句不能包含程序代碼或?qū)Τ绦虼a有間接的影響作用。

            .跟蹤語(yǔ)句的目的是向程序員提供信息,而不是向用戶。

            跟蹤語(yǔ)句也是調(diào)試語(yǔ)句,它可以執(zhí)行程序,并且在運(yùn)行中程序員可以查看變量。跟蹤語(yǔ)句對(duì)于那些使用交互式調(diào)試器很難調(diào)試的程序是很有效的。

            跟蹤語(yǔ)句和斷言的區(qū)別如下:

            .跟蹤語(yǔ)句是無(wú)條件的,斷言是有條件的布爾語(yǔ)句。

            .跟蹤語(yǔ)句用于顯示程序執(zhí)行和變量值,不直接顯示bug,斷言用于顯示出bug。

            .跟蹤語(yǔ)句將信息輸出到調(diào)試窗口或文件中,可被隨意地忽略,斷言打斷程序的執(zhí)行。

            2、MFC中的跟蹤語(yǔ)句

               在MFC中,你可以使用TRACE和AfxOutputDebugString宏、CObject::Dump虛擬函數(shù)和AfxDumpStack函 數(shù)。TRACE宏由AfxDump實(shí)現(xiàn),AfxDump由AfxOutputDebugString實(shí)現(xiàn)。AfxOutputDebugString宏和 AfxDumpStack函數(shù)可以在所有版本中編譯,其他只能在調(diào)試版本中編譯。


            (1)TRACE宏有以下形式:

            1_TRACE(reportType,format);    
            2  
            3_TRACE0(reportType,format,arg1);    
            4  
            5_TRACE1(reportType,format,arg1,arg2);    
            6  
            7_TRACE2(reportType,format,arg1,arg2,arg3);    
            8  
            9_TRACE3(reportType,format,arg1,arg2,arg3,arg4);

            在MFC中,推薦使用TRACEn宏,當(dāng)使用TRACE宏時(shí)需要使用_T宏來(lái)格式化參數(shù)以正確解決Unicode的校正,而TRACEn不需要。

            MFC TRACE宏中的一個(gè)缺點(diǎn)是AfxTrace函數(shù)使用一個(gè)512字符固定大小的緩沖區(qū),這使得它在跟蹤長(zhǎng)字符串時(shí)是無(wú)用的。

            (2)CObject::Dump

            CObject類有一個(gè)轉(zhuǎn)儲(chǔ)(dump)虛擬函數(shù),所有繼承CObject的類都可以通過(guò)重載這個(gè)函數(shù),輸出它們的值。

            3、Visual C++消息Pragma

            消息Pragma實(shí)際上是一個(gè)編譯時(shí)的跟蹤語(yǔ)句,你可以使用它來(lái)警告在預(yù)處理過(guò)程中發(fā)現(xiàn)的潛在的編連(build)問(wèn)題。典型的例子:

            #if (WINVER>=0x0500)

            #pragma message (“NOTE:WINVER has been defined as 0x0500 or greater.”)

            #endif

              消息Pragma是非常有用的,尤其是在復(fù)雜編連中。然而,如果你要檢測(cè)一種特定的問(wèn)題,而不是潛在的問(wèn)題,使用#error預(yù)處理來(lái)代替打斷編譯會(huì)更直接一些。

               每當(dāng)你的程序中有錯(cuò)誤而你想得到更多信息的時(shí)候,你應(yīng)該去查看一下跟蹤消息。由于VC輸出窗口的緩沖區(qū)是有大小限制的,因此如果跟蹤消息數(shù)據(jù)產(chǎn)生的速度 超過(guò)輸出窗口處理的速度,那么消息會(huì)塞滿緩沖區(qū),導(dǎo)致數(shù)據(jù)丟失。避免這個(gè)問(wèn)題的簡(jiǎn)單方法是在輸出大量數(shù)據(jù)的代碼段如轉(zhuǎn)儲(chǔ)對(duì)象時(shí),調(diào)用Sleep API函 數(shù)。

            三、異常

            1、基本概念

              錯(cuò)誤是一種條件,在這種條件下,如果不執(zhí)行額外的處理,線程就不能正常地 執(zhí)行下去。異常是用于處理錯(cuò)誤的。使用異常的一個(gè)很明顯的好處就是它們通過(guò)發(fā)出錯(cuò)誤信號(hào),可以讓程序代碼和錯(cuò)誤處理代碼分開(kāi),而且不會(huì)讓程序忽略錯(cuò)誤,你 不用不斷地檢查函數(shù)的返回值,因此它們將程序代碼簡(jiǎn)單化。另一個(gè)好處是它們不需要嚴(yán)格的編程作風(fēng)。

            異常的基本特性:

            .異常是基于每個(gè)進(jìn)程而提出并處理的。

            .異常不能被線程忽略,必須被處理。

            .未處理的異常會(huì)使進(jìn)程結(jié)束,而不僅僅是結(jié)束線程。

            .異常出來(lái)在釋放棧時(shí)會(huì)釋放所有的棧對(duì)象,避免了資源的漏洞。

            .異常處理需要大量的額外操作,使得它不適于經(jīng)常運(yùn)行的代碼。

            .可以拋出任何類型的異常對(duì)象,除了整數(shù)。

            如果正確執(zhí)行,異常處理有下面的特性:

            .異常是不是正常的運(yùn)行結(jié)果,是特殊情況。

            .異常在返回值無(wú)效的情況下使用。

            .異常是可靠的,不可能被忽略。

            .異常簡(jiǎn)化了錯(cuò)誤處理,簡(jiǎn)化了程序代碼,使錯(cuò)誤處理更加方便。

               Visual C++的默認(rèn)情況下,在調(diào)試版本中處理異常,而在發(fā)布版本中并不進(jìn)行處理。由于異常也是錯(cuò)誤,Windows異常碼采用了同 Windows錯(cuò)誤碼一樣的位映射模式,為一個(gè)32位的值,這些碼由Microsoft定義,任何異常碼的最高四位總是1100(二進(jìn)制),即十六進(jìn)制里 的0xC。

            2、Windows結(jié)構(gòu)異常和C++異常

              Windows結(jié)構(gòu)異常作為硬件異常(如訪問(wèn)非法或被零除)或操 作系統(tǒng)異常的結(jié)果被拋出,C++異常只能由throw語(yǔ)句拋出。Windows結(jié)構(gòu)異常處理不能處理對(duì)象的解析,因此你應(yīng)該在C++程序中一直使用C++ 異常。然而,C++異常不能處理硬件和操作系統(tǒng)異常,你的程序需要將結(jié)構(gòu)異常轉(zhuǎn)化為C++異常。C++異常并不直接從你的程序代碼中拋出而是從C++運(yùn)行 庫(kù)中拋出,因此你需要調(diào)用棧窗口來(lái)返回你的代碼。為了正確處理硬件和操作系統(tǒng)異常,你可以創(chuàng)建自己的異常類并使用_set_se_translator函 數(shù)安裝一個(gè)結(jié)構(gòu)異常向C++異常的轉(zhuǎn)化器,但不要捕獲那些不能恢復(fù)所產(chǎn)生問(wèn)題的轉(zhuǎn)化后的結(jié)構(gòu)異常。

            3、MFC中的異常

               在MFC中,所有的異常對(duì)象都是從CException基類(它有使用起來(lái)非常方便的GetErrorMessage和ReportError成員函 數(shù))中派生來(lái)的。大多數(shù)的MFC異常對(duì)象都是動(dòng)態(tài)分配的,而且當(dāng)它們被捕獲時(shí),必須被刪除,而沒(méi)有被捕獲的MFC異常由MFC本身在 AfxCallWndProc函數(shù)中捕獲并刪除。

            4、異常的開(kāi)銷

              當(dāng)拋出C++異常時(shí),函數(shù)調(diào)用鏈將從此回溯搜索,尋 找可以處理拋出這類異常的處理器。若沒(méi)找到,進(jìn)程結(jié)束。如果找到,調(diào)用棧將被釋放,所有的自動(dòng)(局部)變量也將釋放,然后棧將被整理為異常處理器的上下文 相關(guān)設(shè)備。因此異常開(kāi)銷由一個(gè)異常處理器目錄和一個(gè)活動(dòng)的自動(dòng)變量表(它需要額外的代碼、內(nèi)存,而且不論異常是否拋出,都會(huì)運(yùn)行),還得加上函數(shù)調(diào)用鏈的 搜索、自動(dòng)變量的解析和棧的調(diào)整(它只在拋出異常的時(shí)候需要執(zhí)行)組成。

            5、異常策略

            (1)拋出時(shí)機(jī)

              拋出異常的時(shí)機(jī)應(yīng)該是一個(gè)函數(shù)發(fā)現(xiàn)一個(gè)錯(cuò)誤,如果沒(méi)有一些特殊的操作,該錯(cuò)誤能阻止程序正常的運(yùn)行,而這種操作它自己不能完成,或是在函數(shù)不可能有返回值的時(shí)候。

              使用異常處理更簡(jiǎn)單,更可靠,更有效,可以創(chuàng)建更健壯的代碼。然而,應(yīng)該只在意外的情況下使用異常處理。如果你認(rèn)為一個(gè)指針應(yīng)該是空值,這種條件下就直接在代碼中檢查這個(gè)值,而不要使用異常。

            (2)何時(shí)捕獲

            對(duì)于這個(gè)問(wèn)題,有一些可能的標(biāo)準(zhǔn):

            .當(dāng)函數(shù)知道如何處理這個(gè)異常時(shí)。

            .當(dāng)這個(gè)函數(shù)可以合理地處理這個(gè)異常而高級(jí)的函數(shù)不知道如何處理時(shí)。

            .當(dāng)拋出異常可能使進(jìn)程崩潰時(shí)。

            .當(dāng)函數(shù)可以繼續(xù)執(zhí)行它的任務(wù)時(shí)。

            .當(dāng)需要整理分配好的資源時(shí)。

              異常處理的一個(gè)缺點(diǎn)是它可能導(dǎo)致資源的泄露。因此,防止資源泄露更應(yīng)該是保持程序異常安全的一部分。棧釋放時(shí)會(huì)自動(dòng)整理局部變量,但不包括動(dòng)態(tài)分配的變量。可以使用智能(smart)指針來(lái)保護(hù)你的代碼在存在異常的情況下不會(huì)產(chǎn)生資源泄漏。

            (3)怎樣捕獲

            .非MFC的C++異常應(yīng)該通過(guò)引用來(lái)捕獲。使用引用捕獲異常不需要?jiǎng)h除異常對(duì)象(因?yàn)槭褂靡貌东@的異常會(huì)在棧中傳送),而且它保留了多態(tài)性(因此你捕獲的異常對(duì)象正是你拋出的異常對(duì)象)。

            .MFC 異常應(yīng)該通過(guò)指針來(lái)捕獲。使用指針捕獲異常需要你刪除對(duì)象。因?yàn)樗鼈兺ǔ亩阎蟹峙洌?dāng)你處理完異常之后,需要調(diào)用Delete成員函數(shù)來(lái)刪除。你不可以 使用省略捕獲處理器捕獲MFC異常,這會(huì)導(dǎo)致一個(gè)內(nèi)存泄露。必須使用Delete成員函數(shù)刪除MFC異常,而不用delete,因?yàn)橐恍㎝FC異常為靜態(tài) 對(duì)象創(chuàng)建。

            在釋放棧的過(guò)程中拋出異常會(huì)導(dǎo)致進(jìn)程的終止。釋放棧涉及到調(diào)用析構(gòu)函數(shù),異常可以阻止調(diào)用delete操作符,這樣會(huì)有資源泄漏,因此異常最好不要從析構(gòu)函數(shù)中拋出。如果非要在析構(gòu)函數(shù)里拋出異常,必須妥善處理,避免資源泄漏。

            6、異常與防御性編程

              在異常發(fā)生時(shí)繼續(xù)執(zhí)行程序,遠(yuǎn)比執(zhí)行一個(gè)正常的關(guān)閉動(dòng)作要重要。如果可能,應(yīng)該將精力集中在繼續(xù)執(zhí)行程序,并在必須的情況下才正常地關(guān)閉程序。可能最根本的正常關(guān)閉是一個(gè)在崩潰時(shí)可以重新啟動(dòng)自己的進(jìn)程,這是Windows資源管理器使用的一種技術(shù)。

              如果一個(gè)與錯(cuò)誤相關(guān)的C++異常是可預(yù)料的,如果它發(fā)生在非關(guān)鍵性的代碼中,如果它不是發(fā)生在程序啟動(dòng)或結(jié)束過(guò)程中或一個(gè)不可恢復(fù)的結(jié)構(gòu)異常的結(jié)果中,這個(gè)程序就可以從其中恢復(fù)。

              一旦你的程序可以從與錯(cuò)誤相關(guān)的異常中恢復(fù),應(yīng)該先檢查程序的狀態(tài)和它的文檔。如果程序和文檔已經(jīng)被破壞了,進(jìn)程也應(yīng)該終止運(yùn)行。否則,程序需要通知客戶機(jī)確定動(dòng)作的過(guò)程。如果客戶機(jī)同意執(zhí)行下去,程序應(yīng)該恢復(fù)錯(cuò)誤并繼續(xù)執(zhí)行。

            四、返回值

              并不是在所以場(chǎng)合下都能使用異常,如在使用Windows API編程或帶有COM編程時(shí)并不使用異常。在異常不適合的時(shí)候,使用返回值是一個(gè)好的辦法。

            返回值的基本特性:

            .返回值可以指示正常和不正常的函數(shù)運(yùn)行,但不能阻止線程的繼續(xù)運(yùn)行。

            .返回值很容易被忽略。

            .返回值在典型情況下是一個(gè)整數(shù),通常映射符合于一個(gè)預(yù)定義的值。

            .返回值能高效地傳遞和接收。

            因此,返回值最適合用于以下的情形:

            .用于非錯(cuò)誤的狀態(tài)信息

            .用于大多數(shù)情況下可以隨意忽略而不會(huì)出問(wèn)題的錯(cuò)誤。

            .用于更易于出現(xiàn)在循環(huán)中的錯(cuò)誤。

            .用于中間語(yǔ)言模塊如COM組件中的錯(cuò)誤。

            使用Visual C++調(diào)試器調(diào)試

            一、調(diào)試版本與發(fā)布版本

              有時(shí)程序能在調(diào)試版本運(yùn)行但不能運(yùn)行于發(fā)布版本,反之也有可能。一般說(shuō)來(lái),一個(gè)發(fā)布版本意味著某些類型的優(yōu)化,而一個(gè)調(diào)試版本則沒(méi)有優(yōu)化。下面我們來(lái)看看它們的區(qū)別:

            1、特別針對(duì)調(diào)試版本的編譯選項(xiàng)

            (1)/MDd,/MLd或者/MTd

              調(diào)試版本的運(yùn)行時(shí)刻庫(kù)有調(diào)試符號(hào),使用了調(diào)試堆,調(diào)試堆的目的是發(fā)現(xiàn)內(nèi)存破壞和內(nèi)存泄漏,并且向用戶報(bào)告源代碼的哪個(gè)地方出了問(wèn)題。特性:

            .調(diào)試版本的運(yùn)行時(shí)刻庫(kù)對(duì)內(nèi)存的分配作了跟蹤,允許用戶檢查內(nèi)存泄漏。

            .在剛分配的內(nèi)存里寫上0xCD的字節(jié)模式,用0xCD來(lái)填充剛分配的內(nèi)存,有助于發(fā)現(xiàn)數(shù)據(jù)未被初始化的錯(cuò)誤。

            .在被釋放的內(nèi)存寫上0xDD的字節(jié)模式,有助于發(fā)現(xiàn)已被釋放的內(nèi)存。

            .在緩沖區(qū)的兩邊分配了四字節(jié)的保護(hù)數(shù)據(jù),并用0xFD的字節(jié)模式作初始化,來(lái)檢查寫內(nèi)存的上溢出和下溢出。

            .在每個(gè)內(nèi)存分配的地方對(duì)源代碼文件名和行號(hào)作了記錄,有助于用戶在源代碼中對(duì)內(nèi)存分配進(jìn)行定位。

            (2)/Od

               這個(gè)選項(xiàng)用來(lái)關(guān)閉優(yōu)化開(kāi)關(guān)。因?yàn)槲幢粌?yōu)化的代碼直接對(duì)應(yīng)于源代碼,所以比優(yōu)化后的代碼更容易讀懂。未被優(yōu)化的代碼編譯和鏈接會(huì)更快,會(huì)有更短的調(diào)試周 期。而由于優(yōu)化,發(fā)布版本不見(jiàn)得會(huì)比調(diào)試版本運(yùn)行得好,優(yōu)化代碼要求編譯器做一些假設(shè),去除冗余,但有時(shí)這個(gè)假設(shè)是錯(cuò)誤的,并且去掉的冗余也有可能隱藏錯(cuò) 誤。如發(fā)布版本的幀指針(EBP寄存器)省略(FPO)隱藏了函數(shù)原型不匹配的錯(cuò)誤;在同步異常模式(只能由throw語(yǔ)句拋出,編譯器默認(rèn),由/GX編 譯選項(xiàng)設(shè)置)下,異常處理程序可能被優(yōu)化掉,會(huì)阻止程序中的C++異常處理代碼安全地捕獲結(jié)構(gòu)異常,在這種情況下,你必須使用異步異常模式(采取任何指令 都會(huì)產(chǎn)生異常的機(jī)制,由/Eha編譯選項(xiàng)設(shè)置)。

            (3)/D “_DEBUG”

              打開(kāi)條件編譯調(diào)試代碼開(kāi)關(guān)。只有這個(gè)符號(hào)被定義,調(diào)試代碼才會(huì)被編譯,MFC使用_DEBUG符號(hào)來(lái)確定到底鏈接的是哪個(gè)版本的MFC類庫(kù)。在調(diào)試版本中,內(nèi)聯(lián)默認(rèn)情況下是被關(guān)閉的。

            (4)/ZI

               創(chuàng)建編輯繼續(xù)(Edit and Continue)的程序數(shù)據(jù)庫(kù)。這個(gè)選項(xiàng)會(huì)打開(kāi)/GF編譯選項(xiàng),/GF編譯選項(xiàng)會(huì)消除重復(fù)字符串,并將字符串放到只 讀內(nèi)存。編輯繼續(xù)功能需要獲取存儲(chǔ)在PDB文件里的特殊信息來(lái)使得代碼的修改對(duì)調(diào)試器有效。如果被修改文件對(duì)應(yīng)的信息不在PDB文件里,編輯繼續(xù)功能就不 能進(jìn)行,而且在調(diào)試過(guò)程中對(duì)代碼的任何修改都會(huì)出現(xiàn)下面的提示信息 “One or more files are out of date or do not exist.”。

            (5)/GZ

            在調(diào)試版本中用來(lái)發(fā)現(xiàn)那些在發(fā)布版本里才發(fā)現(xiàn)的錯(cuò)誤。其作用如下:

            .用0xCC模式初始化自動(dòng)(本地)變量。

            .在通過(guò)函數(shù)指針調(diào)用函數(shù)時(shí),檢查棧指針,確認(rèn)是否有調(diào)用規(guī)則不匹配。

            .在函數(shù)最后檢查棧指針是否被改變。

            (6)/Gm

              打開(kāi)最小化重新鏈接開(kāi)關(guān),減少鏈接時(shí)間。

            2、特別針對(duì)發(fā)布版本的編譯選項(xiàng)

            (1)/MD,/ML或者/MT

              使用發(fā)布版本的運(yùn)行時(shí)刻庫(kù)。

            (2)/O1或者/O2

              打開(kāi)優(yōu)化開(kāi)關(guān),使得程序會(huì)最小或說(shuō)速度會(huì)最快,優(yōu)化器還可能發(fā)現(xiàn)代碼中潛在的錯(cuò)誤,而這些錯(cuò)誤可能會(huì)被調(diào)試版本掩蓋。

            (3)/D “NDEBUG”

              關(guān)閉條件編譯調(diào)試代碼開(kāi)關(guān)。

            (4)/GF

              消除重復(fù)字符串并將它們放到只讀內(nèi)存中以避免被錯(cuò)誤地修改。

            (5)/Zi

            創(chuàng)建包含調(diào)試符號(hào)的程序數(shù)據(jù)庫(kù)。

               如果一個(gè)錯(cuò)誤只發(fā)生在發(fā)布版本里,除非你是個(gè)匯編高手,否則你需要調(diào)試符號(hào)來(lái)提示你到底程序出現(xiàn)了什么問(wèn)題,調(diào)試符號(hào)保存在程序的數(shù)據(jù)庫(kù)文件(PDB) 中。Visual C++的AppWizard默認(rèn)情況下沒(méi)有為發(fā)布版本創(chuàng)建調(diào)試符號(hào)。為創(chuàng)建調(diào)試符號(hào),打開(kāi)工程設(shè)置對(duì)話框,選擇 Win32 Release,在C/C++標(biāo)簽里選擇Common類,在調(diào)試信息里,如果是發(fā)布版本選擇Program Database,如果是調(diào)試版 本選擇Program Database for Edit and Continue(編輯繼續(xù)選項(xiàng)與優(yōu)化鏈接不相容,不適于發(fā)布版本)。在Link標(biāo) 簽里選擇Debug類,然后選擇Debug Info和Microsoft format選項(xiàng),最好不要選擇Separate types選項(xiàng),這樣所有 的調(diào)試信息才會(huì)被合并到單獨(dú)的一個(gè)PDB文件中。對(duì)于發(fā)布版本,選擇Link標(biāo)簽,在Project options對(duì)話框的最后加上“/OPT: REF”,這個(gè)選項(xiàng)使得不被引用的函數(shù)和數(shù)據(jù)不會(huì)出現(xiàn)在可執(zhí)行文件中,避免了文件的無(wú)謂增大。對(duì)于調(diào)試版本不要使用這個(gè)選項(xiàng),它會(huì)關(guān)閉增量鏈接 (incremental linking)。

            二、Visual C++編輯器的“設(shè)置”菜單

              當(dāng)你打開(kāi)或新建一個(gè)包 含至少一個(gè)工程的Workspace后,Visual C++的Project菜單中的“Settings…”命令就變?yōu)橛行?,選擇它或者按下熱鍵Alt +F7后,便可調(diào)出工程設(shè)置對(duì)話框,這里面的選項(xiàng)將影響整個(gè)工程的建立和調(diào)試過(guò)程,因此很重要。

              在這個(gè)對(duì)話框中,左上方的下拉列表框 用于選擇一種工程配置,包括有Win32 Debug、Win32 Release和All Configurations(指前兩種配置一起),某些選 項(xiàng)在不同的工程配置中有不同的缺省值。左邊的樹(shù)形視圖給出了當(dāng)前工程所有的文件及分類情況。下面我們就以Win32 Debug為例來(lái)看看與工程有關(guān)的的 四個(gè)主要選項(xiàng)卡的各自功能與含義(一共有十個(gè)選項(xiàng)卡):

            1、 General選項(xiàng)卡

              這個(gè)選項(xiàng)卡比較簡(jiǎn)單,從上向下的 第一個(gè)選項(xiàng)用于更改使用MFC類庫(kù)的方式: DLL的方式或是靜態(tài)連接。我們可以在兩種方式之間進(jìn)行切換。第二個(gè)選項(xiàng)用于指定在編譯連接過(guò)程中生成的中間 文件和輸出文件的存放目錄,對(duì)于調(diào)試版本來(lái)說(shuō),缺省的目錄是工程下面的“Debug”子目錄。第三個(gè)選項(xiàng)用于指定是否允許每種工程配置都有自己的文件依賴 關(guān)系(主要指頭文件),由于絕大多數(shù)工程的調(diào)試版本和發(fā)布版本都具有相同的文件依賴關(guān)系,所以通常不需要更改該選項(xiàng)。

            2、 Debug選項(xiàng)卡

              Debug選項(xiàng)卡中是一些與調(diào)試有關(guān)的選項(xiàng),由于選項(xiàng)比較多,它們被分成了幾個(gè)類,我們可以從Category中選擇不同的類別,選項(xiàng)卡就會(huì)切換顯示出相應(yīng)的選項(xiàng)。

              在General類別中,可以指定要調(diào)試的可執(zhí)行文件名。另外三個(gè)選項(xiàng)可以指定用于調(diào)試的工作目錄,開(kāi)始調(diào)試時(shí)給程序傳送的命令行參數(shù),以及進(jìn)行遠(yuǎn)程調(diào)試時(shí)可執(zhí)行文件的路徑。

            3、C/C++選項(xiàng)卡

               C/C++選項(xiàng)卡控制著Visual C++的編譯器,其中的選項(xiàng)比較多。下面有一個(gè)Project Options編輯框,里面 列出的各種命令開(kāi)關(guān)將會(huì)在開(kāi)始編譯時(shí)作為命令行參數(shù)傳送給Visual C++的編譯器。這些命令開(kāi)關(guān)會(huì)跟隨其它選項(xiàng)改變而改變。

               在General類別中,Warning level用于指定編譯器顯示警告的級(jí)別,如果選中了Warnings as errors,那么顯示的每一 個(gè)警告都將會(huì)引起一個(gè)錯(cuò)誤,這樣在編譯完畢后就無(wú)法啟動(dòng)連接器來(lái)進(jìn)行連接。Optimizations用于設(shè)置代碼優(yōu)化方式,優(yōu)化的目的主要有提高運(yùn)行速 度和減小程序體積兩種,但有時(shí)候這兩種目的是相互矛盾的。另外,在極少數(shù)情況下,不進(jìn)行優(yōu)化,程序能正常運(yùn)行,打開(kāi)了優(yōu)化措施之后,程序卻會(huì)出現(xiàn)一些莫名 其妙的問(wèn)題。其實(shí)這多半是程序中有潛在的錯(cuò)誤,關(guān)閉優(yōu)化措施往往只是暫時(shí)解決問(wèn)題。Debug info用于指定編譯器產(chǎn)生的調(diào)試信息的類型,為了使用 Visual C++的即編即調(diào)功能,必須在這里選擇生成“Program Database for Edit and Continue”類型的調(diào)試 信息。Preprocessor definitions是一些預(yù)先定義的宏名。

              C++ Language類別中的選項(xiàng)涉及到了C+ +語(yǔ)言的一些高級(jí)特性,包括有成員指針的表示方式、異常處理、運(yùn)行時(shí)類型信息,一般情況下都不用改變它們。Code Generation類別中的選項(xiàng)涉 及如何生成目標(biāo)代碼,一般情況下保持缺省值即可。在Customize類別中,從上到下六個(gè)選項(xiàng)的含義分別為:是否禁止使用Microsoft對(duì)C++的 擴(kuò)展;是否允許函數(shù)級(jí)別的連接;是否消除重復(fù)的字符串;是否允許進(jìn)行最小化的重建;是否允許遞增編譯方式;是否允許編譯器在開(kāi)始運(yùn)行時(shí)向Output窗口 中輸出自己的版本信息。

              在Listing Files類別中,我們可以指定編譯器生成瀏覽信息和列表文件 (Listing file),前者可由瀏覽信息維護(hù)工具BSCMAKE生成瀏覽信息文件,后者則包含了C/C++源文件經(jīng)過(guò)編譯后對(duì)應(yīng)的匯編指令。 Optimizations類別允許我們對(duì)優(yōu)化措施進(jìn)行更細(xì)微的控制,選擇了Customize后,便可以選擇進(jìn)行哪幾項(xiàng)優(yōu)化,在 Inline function expansion中我們可以指定對(duì)內(nèi)聯(lián)函數(shù)的擴(kuò)展方式。Precompiled Headers類別中是關(guān)于預(yù)編譯頭 文件的一些選項(xiàng),一般情況下都不用更改。Preprocessor類別中是關(guān)于預(yù)處理的一些選擇。

            4、Link 選項(xiàng)卡

               Link選項(xiàng)卡控制著Visual C++的連接器。在General類別中,可以指定輸出的文件名,以及一些在連接過(guò)程中需要使用的額外的庫(kù)文件或目 標(biāo)文件,下邊五個(gè)選項(xiàng)的含義分別為:生成調(diào)試信息;忽略所有缺省的庫(kù)文件;允許遞增連接方式(這種方式可以加快連接的速度);生成MAP文件;允許進(jìn)行性 能分析。在Customize中選中Use program database允許使用程序數(shù)據(jù)庫(kù)。在Debug類別中,我們可以指定調(diào)試信息的類別是 Microsoft的格式,還是COFF格式,或者兩種都有,選中Separate types后連接器會(huì)把調(diào)試信息分開(kāi)放在PDB文件中,這樣連接起來(lái) 會(huì)更快一些,但調(diào)試時(shí)速度卻會(huì)慢一些。Input類別中是一些與輸入庫(kù)文件有關(guān)的選項(xiàng),我們可以在這里指定使用或不使用某些庫(kù)文件或目標(biāo)文件。 Output類別中則是一些與最終輸出的可執(zhí)行文件有關(guān)的選項(xiàng),一般情況下都不用改變。

            三、Visual C++調(diào)試工具

            1、調(diào)試窗口

            (1)觀察窗口(Watch)

              調(diào)試程序時(shí),可使用觀察窗口監(jiān)視變量和表達(dá)式。

            (2)快速查看窗口(Quick watch)

              功能和觀察窗口差不多。

            (3)變量窗口(Variables)

              變量窗口有三個(gè)標(biāo)簽:Auto標(biāo)簽顯示了當(dāng)前語(yǔ)句和前一條語(yǔ)句用到的變量,Locals標(biāo)簽顯示當(dāng)前函數(shù)的局部變量,this標(biāo)簽顯示了this指針執(zhí)行的對(duì)象。

            (4)寄存器窗口(Register)

              可以監(jiān)視CPU的寄存器、標(biāo)志值以及浮點(diǎn)堆棧

            (5)內(nèi)存窗口(Memory)

              可顯示從一特定地址開(kāi)始的虛擬內(nèi)存。Address框允許你指定從哪個(gè)虛擬內(nèi)存地址開(kāi)始顯示。

            (6)調(diào)用棧窗口(Call stack)

              可顯示引起當(dāng)前源代碼語(yǔ)句執(zhí)行的一系列函數(shù)調(diào)用,當(dāng)前函數(shù)在堆棧的頂端。

            (7)反匯編窗口(Disassembly)

              可查看編譯器生成的對(duì)應(yīng)于源代碼的匯編指令。

            2、調(diào)試符號(hào)

               程序數(shù)據(jù)庫(kù)文件(.pdb)包含了Visual C++調(diào)試器所需的調(diào)試信息和程序信息。調(diào)試信息包含了變量的名字和類型、函數(shù)原型、源代碼行號(hào)、類和 結(jié)構(gòu)的布局、FPO調(diào)試信息(重建堆棧幀)以及進(jìn)行增量鏈接所需的信息。對(duì)于設(shè)置了 Program Database for Edit and Continue選項(xiàng)的程序,PDB還要包含執(zhí)行編輯繼續(xù)功能所需的信息。

            3、使用斷點(diǎn)

              斷點(diǎn)(BreakPoint)是運(yùn)行你向調(diào)試器描述環(huán)境,并讓調(diào)試器設(shè)置好程序狀態(tài)的一種機(jī)制。如果沒(méi)有斷點(diǎn),只有在程序里一步一步跟蹤使用調(diào)試器。在Visual C++中,你可以設(shè)置三種類型的斷點(diǎn):代碼定位斷點(diǎn)、數(shù)據(jù)斷點(diǎn)和消息斷點(diǎn)。

            四、提高調(diào)試器的查錯(cuò)能力

              盡量采用編譯時(shí)刻檢查而不是運(yùn)行時(shí)刻檢查。

            1、使用最高的編譯警告級(jí)別/W4

               象if(x=2)這樣的語(yǔ)句,默認(rèn)的警告級(jí)別為/W3時(shí)不顯示任何信息,但改成最高警告級(jí)別/W4時(shí)則會(huì)出現(xiàn)“waning C4706: assignment within conditional expression”的警告。/W4能給出一些/W3所不能給的警告。

            2、在調(diào)試版本中使用/GZ編譯選項(xiàng)

              /GZ選項(xiàng)用來(lái)發(fā)現(xiàn)那些在發(fā)布版本里才發(fā)現(xiàn)的錯(cuò)誤,包括未被初始化的自動(dòng)(局部)變量、堆棧錯(cuò)誤、不正確的函數(shù)原型等。

            3、使用#pragma warning編譯器指示

              你可以使用#pragma warning編譯器指示來(lái)禁止整個(gè)程序、特定的頭文件、特定的代碼文件或是特定的某一行代碼的特定警告,這看你把#pragma放在哪里。

            4、使用沒(méi)有警告的編譯法則/WX

              這個(gè)編譯選項(xiàng)把所有的警告當(dāng)成錯(cuò)誤來(lái)對(duì)待,只有在假警告被消除之后才能應(yīng)用。有時(shí)編譯警告可能是合理的,處理編譯警告的核心是要發(fā)現(xiàn)錯(cuò)誤,而不是抑制警告本身。這個(gè)法則對(duì)于大的程序開(kāi)發(fā)小組來(lái)說(shuō)很有幫助。最終目標(biāo)是消除錯(cuò)誤,而不是消除警告。

            五、內(nèi)存空間與分配

            1、內(nèi)存分配錯(cuò)誤

              動(dòng)態(tài)內(nèi)存分配錯(cuò)誤有兩種基本類型:內(nèi)存錯(cuò)誤和內(nèi)存泄漏。

            (1)內(nèi)存錯(cuò)誤

               當(dāng)一個(gè)指針或者該指針?biāo)赶虻膬?nèi)存單元成為無(wú)效單元,或者內(nèi)存中分配的數(shù)據(jù)結(jié)構(gòu)被破壞時(shí),就會(huì)造成內(nèi)存錯(cuò)誤。指針未被初始化,指針被初始化為一個(gè)無(wú)效地 址,指針被不小心錯(cuò)誤地修改,在與指針相關(guān)聯(lián)的內(nèi)存區(qū)域被釋放后使用該指針(這種指針被稱為虛懸(dangling)指針),這些都會(huì)使指針變?yōu)闊o(wú)效指 針。當(dāng)通過(guò)一個(gè)錯(cuò)誤指針或者虛懸指針對(duì)內(nèi)存進(jìn)行寫入,或者將指針強(qiáng)制轉(zhuǎn)換為不匹配的數(shù)據(jù)結(jié)構(gòu),又或者是寫數(shù)據(jù)越界,內(nèi)存自身也會(huì)遭到破壞。刪除未被初始化 的指針、刪除非堆指針、多次刪除同一指針或者覆蓋一個(gè)指針的內(nèi)部數(shù)據(jù)結(jié)構(gòu),都會(huì)造成內(nèi)存分配系統(tǒng)錯(cuò)誤。

            (2)內(nèi)存泄漏

              內(nèi)存泄漏在被動(dòng)態(tài)分配的內(nèi)存沒(méi)有被釋放時(shí)產(chǎn)生。有許多情況會(huì)導(dǎo)致內(nèi)存泄漏,如沒(méi)有在程序的全部執(zhí)行路徑中釋放內(nèi)存,沒(méi)有在析構(gòu)函數(shù)中釋放所有的內(nèi)存等。一個(gè)程序在崩潰之前可運(yùn)行的時(shí)間越長(zhǎng),則導(dǎo)致崩潰的原因與內(nèi)存泄漏的關(guān)系越大。

               Windows會(huì)在程序結(jié)束的時(shí)候?qū)⑿孤┑膬?nèi)存收回,因此內(nèi)存泄漏是個(gè)暫時(shí)性的問(wèn)題。但為什么必須消除內(nèi)存泄露呢?首先,內(nèi)存泄漏往往會(huì)導(dǎo)致系統(tǒng)資源的 泄漏。動(dòng)態(tài)分配內(nèi)存往往不僅僅代表一塊存儲(chǔ)區(qū)域,還代表了某些類型的系統(tǒng)資源,如文件、窗口、設(shè)備上下文、GDI對(duì)象等。其次,高質(zhì)量的程序和特定的服務(wù) 器程序必須能夠無(wú)限地運(yùn)行下去。最后,內(nèi)存泄漏往往是其他程序錯(cuò)誤或不良編程習(xí)慣的征兆。

              導(dǎo)致內(nèi)參泄漏的原因:忘記釋放內(nèi)存;構(gòu)造函數(shù)失?。淮嬖趦?nèi)存泄漏的析構(gòu)函數(shù);存在內(nèi)存泄漏的異常處理程序;多個(gè)返回語(yǔ)句;使用錯(cuò)誤形式的delete。

            2、關(guān)于內(nèi)存的初始化

              在調(diào)試版本里,堆里未被初始化的內(nèi)存被0xCD字節(jié)模式填充,堆里釋放的內(nèi)存被0xDD字節(jié)模式填充。堆棧里被初始化的內(nèi)存被0xCC字節(jié)模式填充。調(diào)試版本和發(fā)布版本里,未被初始化的全局內(nèi)存都被初始化為0。

            3、內(nèi)存虛擬地址空間

              Windows使用一組固定的范圍來(lái)分割進(jìn)程的4GB虛擬地址空間,因此有時(shí)可通過(guò)查看指針的返回值來(lái)判斷指針是否有效。

            (1)Windows2000虛擬地址空間劃分

            0~0XFFFF(64KB):不能用來(lái)檢測(cè)空指針賦值(訪問(wèn)沖突)

            0x10000(64KB)~0x7FFEFFFF(2GB-64KB):Win32進(jìn)程私有的(非保留的),用于程序代碼和數(shù)據(jù)

            0x7FFF0000(2GB-64KB)~0x7FFFFFFF(2GB):不能用來(lái)防止覆蓋OS分區(qū)(訪問(wèn)沖突)

            0x800000000(2GB)~0xFFFFFFFF(4GB):為操作系統(tǒng)保留,不可訪問(wèn)(訪問(wèn)沖突)

            (2)Windows2000虛擬地址空間使用

            0x00030000~0x0012FFFF:線程棧

            0x00130000~0x003FFFFF:堆(有時(shí)堆位于此處)

            0x00400000~0x005FFFFF:可執(zhí)行代碼

            0x00600000~0x0FFFFFFF:堆(有時(shí)堆位于此處)

            0x10000000~0x5FFFFFFF:App DLLs、Msvcrt.dll、Mfc42.dll

            0x77000000~0xFFFFFFFF:Advapi32.dll、Comctl32.dll、Gdi32.dll、Kernel32.dll、Ntdll.dll、Rpcrt4.dll、Shell32.dll、User32.dll

            其中,0x00400000是所有版本的Windows能使用的最低基地址。

            六、一些調(diào)試技術(shù)

            1、調(diào)試死循環(huán)

              使用Debug菜單下的Break命令。在Windows2000中,如果程序有輸入請(qǐng)求,可以使用F12鍵中斷程序,然后檢查窗口的調(diào)用棧,或單步跟蹤代碼找到死循環(huán)的發(fā)生原因。

            2、用Spy++調(diào)試與消息有關(guān)的問(wèn)題

               調(diào)試消息的最好方案是使用Visual C++提供的Spy++工具。Spy++允許程序員查看窗口、消息、進(jìn)程和線程。Spy++默認(rèn)的消息輸出:第 一欄顯示行號(hào)。第二欄顯示接受消息的句柄。第三欄中的“S”表示消息是用SendMessage發(fā)出的,“P”代表消息是由PostMessage發(fā)出 的,“R”是消息句柄的返回值。第四欄給出解碼后的消息名,消息參數(shù)或返回值。

            3、非常規(guī)方法

            (1)重新編連你的應(yīng)用程序

              當(dāng)你的程序表現(xiàn)出異常的或意外的行為,或者Visual C++編譯器因?yàn)橐粋€(gè)內(nèi)部編譯器錯(cuò)誤而失敗時(shí),最好刪除工程中的Debug或Release文件夾,從頭開(kāi)始重新進(jìn)行編連。

            (2)重新啟動(dòng)Visual C++

               Visual C++有超強(qiáng)的能力,但編譯器的某些特性也會(huì)引起奇怪的錯(cuò)誤。如果你的程序表現(xiàn)得很奇怪,你可是試著清除所有的斷點(diǎn),關(guān)閉或隱藏觀察窗 口,檢查工程設(shè)置對(duì)話框看最近做了什么修改,直至重新啟動(dòng)Visual C++以便消除由于Visual C++環(huán)境引起的異常行為。

            (3)重新啟動(dòng)Windows

              當(dāng)你發(fā)現(xiàn)Windows或者其他程序表現(xiàn)出異常的或出人意料的行為時(shí),就應(yīng)該重新啟動(dòng)Windows,以消除操作系統(tǒng)給調(diào)試帶來(lái)的干擾。

            posted on 2007-05-29 14:51 北風(fēng)之神007 閱讀(1167) 評(píng)論(0)  編輯 收藏 引用 所屬分類: c/c++
            欧美一级久久久久久久大片| 久久国产精品国语对白| 中文无码久久精品| 国产成人精品久久免费动漫| 品成人欧美大片久久国产欧美... 品成人欧美大片久久国产欧美 | 久久国产精品77777| 久久国产一区二区| 久久久噜噜噜久久中文字幕色伊伊 | 久久综合亚洲欧美成人| 国产成人久久777777| 久久精品青青草原伊人| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 国产日产久久高清欧美一区| 久久电影网一区| 久久99精品久久久大学生| 一本大道久久a久久精品综合| 午夜精品久久久久久| av午夜福利一片免费看久久| 热久久国产欧美一区二区精品| 久久九九兔免费精品6| 性做久久久久久久久老女人| 国产亚洲综合久久系列| 97精品伊人久久久大香线蕉| 久久久久国色AV免费观看| 色综合合久久天天综合绕视看| 人妻精品久久久久中文字幕一冢本 | 99久久精品费精品国产| 成人国内精品久久久久影院| 久久婷婷国产剧情内射白浆| 久久中文字幕无码专区| 久久狠狠一本精品综合网| 国产亚洲美女精品久久久久狼| 一本色道久久综合狠狠躁| 久久亚洲国产精品成人AV秋霞| 精品国产乱码久久久久久浪潮| 久久99毛片免费观看不卡 | 欧美激情精品久久久久久| 久久婷婷人人澡人人| 久久亚洲国产成人精品无码区| 久久播电影网| 2021国产精品午夜久久|