評C/C++實戰之記憶體管理 (轉)
C/C++實戰之管理
關鍵字:
C++,記憶體分配,
貼文時間 2001-11-11 21:23:47
原作 Paul_Ni
歡迎大家來到這片大多數員都心有餘悸的雷區。本世紀偉大的比爾·蓋茨曾經失言:
640K ought to be enough for everybody -- Bill Gates 1981
相信程式設計師們都經常要編寫一些關於記憶體分配和使用的程式,而且都有過那種生不如死的感覺(當然我是指那種程式的感覺了,可能誇張了些!)
常見的記憶體分配和使用錯誤
1) 記憶體的申請和分配並沒有成功,但程式設計師卻使用了它。一些新手經常會犯這種錯誤,他們並不會留意到記憶體沒有分配成功。判斷指標的值是否為NULL可以有效地避免這種錯誤。
2) 記憶體的分配已經成功,但是卻沒有進行初始化就直接使用它了。首先是觀念上的問題,很多人都沒有在使用指標前要初始化這樣的習慣,然而這個習慣卻是很重要的,希望大家一定要強迫自己養成。第二就是主觀地認為自己申請的記憶體的預設值為0,這樣想是沒有什麼道理的,記憶體分配後的值是不確定的。
3) 上面的兩種工作都已經做好了(已經成功申請並初始化完成),但是操作時卻越界了。
4) 申請了記憶體,使用完了卻忘記了釋放,導致記憶體洩露。這樣的錯誤可以形容為一個惡性的腫瘤,它不會馬上要你的命,但是它會慢慢地吞噬你的系統資源,直到你的程式徹底完蛋。
5) 你很小心地釋放了記憶體,但是卻又使用了它。由於程式很複雜或者順序出錯,這樣可能導致出現上面的錯誤。
指標---一把偉大的雙刃劍
我真的非常佩服發明指標的人,他簡直太偉大了。能使用如此簡潔地方法將複雜的記憶體結構描述的如此清楚,這本身就是一種偉大的成就。但是,指標之於程式設計師如同武器之於士兵,用好了可以威力無比,用不好則害人害己。
我先說說指標和陣列的區別。陣列名對應著一塊記憶體,它的地址、容量在其生命週期中是不可變的,只有陣列內容是可變的。指標可隨時指向任何型別的記憶體,它的特點就是“變”。指標遠比陣列靈活,但也更危險。
陣列名是不能直接進行賦值和比較的。如果你向要將陣列a賦值給陣列b,不能直接用賦值語句b = a ,這樣會令產生錯誤的。必須使用標準的庫strcpy來進行賦值。相同地,要比較a和b的內容是否相同,不能使用普通的邏輯判斷if(b==a),也要應用庫函式strcmp來判斷。
//陣列……
char a[] = “hello”;
char b[100];
strcpy(b, a); // b = a is wrong
if (strcmp(b, a) == 0) //if (b == a) is wrong
cout<
//指標……
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p, a);
if (strcmp(p, a) == 0)
cout<
free(p);
在計算記憶體容量的時候有一點是必須要指出的,那就是sizeof計算陣列是計算它的實際的記憶體容量,而計算指標時則永遠都是4個位元組。C++是永遠沒有辦法直到指標所指的記憶體容量,除非在申請時記住它。
free和delete如何對付指標?
程式設計師都知道它們是用來釋放申請的記憶體的,但是卻很少有人注意到指標本身並沒有發生什麼變化。各位可以在VC中使用單步跟蹤一下,你們會驚奇地發現當指標p被呼叫了free後它的地址值並沒有改變,只是該地址對應的記憶體中原來有意義的值變成了垃圾,“p”卻還是指向的這塊記憶體。記住,一定要第一時間將p的值設為NULL,否則會讓別人以為p是一個有意義的指標而誤使用它(當別人使用該指標時會判斷指標的值是否為NULL,如果不為NULL就會以為它有意義)。
char *p = (char *)malloc(100);
strcpy(p, “hello”);
free(p); // the address of “p” is not changed.
….
if (NULL != p) //it will return TRUE
strcpy(p, “world”); //Wrong!!!
下面提兩點,讓大家可以防止上面的情況出現:
1) 指標宣告後要馬上初始化。因為指標出現的預設值是隨機的,所以一定要賦值為NULL,然後再使用。
2) 呼叫了free和delete後一定要將指標賦值為NULL。原因上面已經提過了,就不再贅述了。
本文首先分析了使用記憶體會出現的常見錯誤。然後論述了記憶體使用過程中最為關鍵的一環 — 指標的一些平時不為人注意的用法和技巧。這些都是我平時在做工程專案中積累下的,希望能對大家(特別是那些還在記憶體的苦海中掙扎的苦難弟兄們)會有所幫助。有什麼經驗和問題需要交流的,請我。">paulni@citiz.net
對該文的評論
sunny2001 ( 2001-11-13 16:22:46 )
很好,拋磚引玉,謝謝各位大俠!
Wind_LQ ( 2001-11-12 19:58:34 )
建議C初學者仔細看看,在學到一些基本常識的同時也順便提高自己的除蟲能力。
wilaby ( 2001-11-12 17:26:17 )
無賴騙點。我還以為什麼高手寫的呢!!!這些基礎教小學生的吧???
andy_show ( 2001-11-12 16:23:22 )
相信有許多文章提到過智慧指標的用法,STL的auto_ptr是最好的例子,透過一個Template類實現大部分用new分配的記憶體的自動釋放。另外一個辦法是透過實現一個Reference Wrapper的Template類來實現基於引用計數的資源的自動釋放,不過開銷要比auto_ptr大,但是好處是可以實現對不同型別的系統資源的自動回收(如系統中的記憶體,GDI,等Handle)。關於如何實現這樣的類可以參考一下VC中_bstr_t的類的實現,雖然這個類只是侷限於對於COM的BSTR型別的封裝,不過其中的思想完全可以應用到像Handle那樣的型別中。另為一個比較好的例子是_com_ptr_t的類,應該可以給各位不少啟發。其實只要善用C++的特性,完全可以避免記憶體洩漏。不過,類似陣列越界這樣的問題還是無法避免。
thunder_yu ( 2001-11-12 14:38:36 )
呵呵,用了<叫C實戰之記憶體管理更好一些。
TopCat ( 2001-11-12 12:29:01 )
指出你文章中的幾點小錯誤:
1. 記憶體分配失敗返回NULL只有當你使用malloc函式家族時才正確,如果使用new,現在標準C++的做法是丟擲一個std::bad_alloc型別的異常。
2. strcpy()函式只對字元指標才有效,因為它的結束條件是判斷(*)+x == ',如果把它用在其它型別的指標中,哼哼……,其實應該用memcpy()。strcmp也用相同的問題。
3. C++是不推薦使用malloc/free家族的,因為他們無法呼叫類的constructor和destructor,但你的程式碼中從來只出現了malloc/free,這起碼讓你的題目有一半名不副實。
4. 其實C++中的指標使用方法和注意事項與C中的區別還是非常大的。C++中更多的是考慮多型以及ctor/dtor的問題,你這篇文章可以說完全展現的是C的指標問題。(其實如上所說的陣列問題,C++中可以使用vector和string最大限度的避免指標)。
我的評價:
在C++中,我從來都不這樣做。而是:
#define SAFE_DELETE(p) (delete (p),(p)=NULL)
#if _DE
#if defined(new) && defined(MYNEW)
#undef new
#endif
void * _cdecl operator new (size_t nSize,LPCSTR pFileName,D dwLine)
{
1。使用其他記憶體分配函式分配記憶體
2。做一些操作,記錄記憶體分配情況和分配次數
3。記錄檔案位置
4。小技巧:
_onexit();
怎麼用?查查MSDN,或問問其他人
}
inline void * _cdecl operator new (size_t nSize)
{
同上,但是沒有檔案位置
}
inline void _cdecl operator delete (void * pMem,LPCSTR pFileName,DWORD dwLine)
{
1。使用其他記憶體釋放函式
2。根據記錄的記憶體分配資訊來初步確定記憶體的合法性、越界等並輸出錯誤資訊
3。VC中的小技巧(其他的編譯器就不知道了):
char buff[1024];
sprintf(buff,"%s(%d) : 出錯資訊n",pFileName,dwLine,...);
OutputDebugString(buff);
這樣,可以讓VC直接跳到出錯程式碼處,當然,僅限於除錯執行(F5)並且MFC Trace中設定了輸出除錯資訊。
}
inline void _cdecl operator delete (void * pMem)
{
1和2同上。
3。輸出分配這塊記憶體是第幾次分配的,然後利用 的條件斷點跟蹤出出錯程式碼
}
#ifdef MYNEW
#define new MYNEW
#else
#define MYNEW new(__FILE__,__LINE__)
#endif
#endif //end _DEBUG
透過以上步驟,基本可以在除錯版本杜絕記憶體使用錯誤
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990420/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C/C++實戰之記憶體管理 (轉)C++記憶體
- 轉載——C++記憶體管理C++記憶體
- C++記憶體管理C++記憶體
- C++記憶體管理剖析C++記憶體
- C++記憶體管理:簡易記憶體池的實現C++記憶體
- 【C/C++】4.C++的記憶體管理C++記憶體
- C++學習體會:記憶體管理C++記憶體
- 記憶體管理兩部曲之實體記憶體管理記憶體
- 記憶體管理之五 (轉)記憶體
- C++動態記憶體管理——new/deleteC++記憶體delete
- C++記憶體管理:new / delete 和 cookieC++記憶體deleteCookie
- C++記憶體模型實踐探索C++記憶體模型
- [C++]記憶體分配C++記憶體
- C++實戰之程式書法 (轉)C++
- C++實戰之資料抽象 (轉)C++抽象
- c++之引用及記憶體分割槽模型C++記憶體模型
- C++ 記憶體對齊C++記憶體
- c++ 記憶體 繼承C++記憶體繼承
- C++記憶體掃描C++記憶體
- C++動態記憶體管理與原始碼剖析C++記憶體原始碼
- c++動態記憶體管理與智慧指標C++記憶體指標
- C和C++的動態記憶體管理的區別C++記憶體
- 記憶體管理篇——實體記憶體的管理記憶體
- C++記憶體池的實現例項C++記憶體
- C# 記憶體管理C#記憶體
- C++手寫記憶體池C++記憶體
- 控制C++的記憶體分配C++記憶體
- C++ 動態記憶體分配C++記憶體
- C++中“記憶體重疊”C++記憶體
- C++動態記憶體分配C++記憶體
- Objective-C 記憶體管理之ARC規則Object記憶體
- C/C++記憶體對齊詳解C++記憶體
- C/C++記憶體分配以及釋放C++記憶體
- C/C++記憶體對齊原則C++記憶體
- 記憶體管理兩部曲之虛擬記憶體管理記憶體
- JAVA記憶體管理 [轉]Java記憶體
- Objective-C 記憶體管理之alloc/retain/release/dealloc實現原理Object記憶體AI
- 【C++】C++ 記憶體分配(new,operator new)詳解C++記憶體