標籤:style blog color io 使用 ar 檔案 div sp
TRACE 使用方法與printf完全一致,在output框中輸出調試資訊
ASSERT 接收一個運算式,如果運算式為true,則無動作,否則終端當前程式執行,對於系統中出現這個宏導致的中斷,應該認為函數調用未能滿足系統的調用此函數的前提條件,例如,對於一個沒有建立的視窗調用setwindowtext
VERIFY和ASSERT功能類似,所不同的是,在Release版本中,ASSERT不計算運算式的值,而VERIFY計算運算式的值
幾個關於C/C++調試用到的函數
1調用棧系列
下面是函數原型
#include "execinfo .h"int backtrace(void **buffer, int size);char **backtrace_symbols(void *const *buffer, int size);void backtrace_symbols_fd(void *const *buffer, int size, int fd);
(1)backtrace用來擷取當前程式的調用棧,把結果存在buffer中。通常,我們用gdb偵錯工具,設定合適的斷點,停下來之後,用backtrce命令,就可以看到當前的調用棧,但是有時候,用到條件斷點的時候。gdb的功能就沒有程式本身的功能強大了,這個時候就可以考慮在程式中調用backtrace函數,來擷取調用棧。
(2)backtrace_symbols把用backtrace擷取的調用棧轉換成字串數組,以字串數組的形式返回,使用這需要在外面釋放返回的字串數組所佔用的記憶體
(3)backtrace_symbols_fd把用backtrace擷取的調用棧資訊寫到fd所指定的檔案中
void * __builtin_return_address (unsigned int level)
這個函數用來得到當前函數,或者調用它的函數的返回地址,得到這個地址後,通過gdb反組譯碼,便可得到調用函數相關的資訊,這也是在應用中擷取調用棧的一種fangfa。
2記憶體配置釋放
#include "malloc .h"size_t malloc_usable_size((void *__ptr));
這個函數的用法是返回調用malloc後實際分配的可用記憶體的大小,我們知道在c++中,operator new()可以重載各種各樣的版本,可以傳入調用的相關資訊來跟蹤記憶體配置情況。但是operator delete()卻只有兩種形式,不能隨意重載,尤其是全域的operator delete(),只有一種版本,這個時候就比較痛苦了,究竟釋放了多少記憶體,這時候malloc_usable_size()這個函數就有用武之地了,調用它就可以擷取當前釋放的記憶體大小,那麼在new中也請用他來統計開闢的記憶體,這樣才能對應起來,因為在調用malloc時很多時候實際分配記憶體會比使用者申請的要大一些,所以如果兩邊的統計方法對應不起來的話,統計結果也有比較大的判別
C++記憶體配置與管理
1函數
(1)operator new function
void * ::operator new(size_t); //Globalvoid * class-name::operator new(size_t); //Class
上面是C++中operator new function的原型,一個是全域類型,一個的類成員類型,全域類型的operator new 函數在下面兩種情況下被調用:一種是在分配C++內建類型的動態記憶體時,一種是在分配使用者自己定義的operator new函數,那麼使用者在用new申請該類型的動態記憶體時,便會調用該類型的成員函數operator new,而不是全域的operator new。
另外,我們注意到,上面的原型中函數的傳回值void* 類型,第一個參數為size_t類型,這個是C++編譯要求的,如果要自己重載operator new函數,傳回值必須為void* 類型,第一個參數必須為size_t類型,否則,編譯器會返回如下錯誤資訊:
error: ‘operator new’ takes type ‘size_t’ (‘unsigned int’) as first parameter
這裡需要注意的一點是,我們可以利用operator new function可以重載的特點,可以通過參數傳入一些額外的資訊,來偵錯工具,檢測記憶體泄露,比如,我們可以向下面這樣重載,傳調用處的行號,函數名,這樣就可以跟蹤記憶體的分配
void * operator new(size_t unSize, int nLine, const char * pFunc){ prinft("Line: %d, Func: %s, allocate %u byte(s)\n", nLine, pFunc, unSize); return malloc(unSize);}
(2)operator delete function
void operator delete( void * );void operator delete( void *, size_t );
上面是operator delete function的原型,operator delete function也有全域和類成員的兩種,這壓力需要注意,一個類只能有一個operator delete function作為其成員函數,而且必須為上面兩種中的其中一種,沒有其他的形式,如果一個類實現了自己的operator delete function成員函數,那麼在釋放該類型的記憶體時,編譯器便會調用成員operator delete function,而不是全域的。
上面兩種原型,第一種在調用的時候,編譯器會把釋放的記憶體的首地址傳入,第二種在調用的時候,編譯器會把要釋放的記憶體的首地址和大小都傳入,因此,可以利用這一特性,如果我們在基類中實現第二種形式的operator delete function的成員函數,那麼便可以之來釋放子類類型的記憶體。
2 運算子
(1)new operator
[::] new [placement] new-type-name [new-initializer][::] new [placement] ( type-name ) [new-initializer]
上面是new operator 的原型,在c++中,動態記憶體的分配,通常都是調用new operator來完成的,利用new operator來分配動態記憶體,編譯器要做下面兩項工作:
a. 調用operator new function 分配記憶體
b. 調用建構函式來進行初始化
下面來說一說new operator的原型中各部分到底是幹什麼的:
placement:如果你重載了operator new function,placement可以用來傳遞額外的參數
type-name:指定要分配的記憶體類型,可以是內建類型,也可以是使用者自訂類型
new-intializer:指定要分配後的記憶體的初始化參數,也就是建構函式的參數。這裡需要注意一點,在分配一個對象的數群組類型的記憶體時,不能夠指定初始化參數:換言之,想要分配一個對象的數群組類型的記憶體,該對象必須有預設建構函式
(2)delete operator
[::] delete cast-expression[::] delete [ ] cast-expression
上面是delete operator的原型,第一種是用來釋放普通的物件類型的記憶體,第二種是用來釋放對象的數群組類型的記憶體,在C++中,用new operator分配的動態記憶體,必須調用delete operator來釋放,通常用delete operator 釋放記憶體編譯器要做下面兩箱工作:
a 調用對象解構函式來析構對象
b 調用operator delete function 來釋放記憶體
3關於new/delete使用過程中一些需要注意的點
(1)如何區別operator new/ delete function與new/delete operator?
通過上面的講述,不難看出,我們分配/釋放動態記憶體,調用的是new/delete operator, 而在調用new/delete的過程中,編譯器會自動調用operator new/delete function來完成實際的記憶體配置/釋放的工作
(2) 用delete operator去釋放一塊不是由new operator釋放的記憶體,結果是不可預料的,因此,切記,operator new與operator delete一定要配對使用,這是寫好程式的基礎
(3) new operator調用失敗會拋出std::bad_alloc異常,前提是你沒有自己重載對應的operator new function;delete operator失敗,常見的原因是多次delete同一塊記憶體
(4) 如果一塊記憶體被delete後,再對它解引用(Dereference),結果也是不可預測的,很可能導致程式崩潰
(5) delete一個空(NULL)指標是安全的,沒有任何害處的
(6) 類成員類型的operator new/delete函數必須為靜態(static)函數,因此它們不能為虛函數(virtual function),也遵守public, protected, private的存取權限控制
VC程式調試的方法