C++記憶體配置秘籍—new,malloc,GlobalAlloc的區別與詳解

來源:互聯網
上載者:User

(來源:http://www.cnblogs.com/gaochaooo/archive/2009/09/03/1559764.html

               C++記憶體配置秘籍—new,malloc,GlobalAlloc詳解
                                                   _______只為因記憶體配置而無法入眠的程式員

一。關於記憶體

 1、記憶體配置方式

  記憶體配置方式有三種:

  (1)從靜態儲存地區分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個運行期間都存在

。例如全域變數,static變數。

  (2)在棧上建立。在執行函數時,函數內局部變數的儲存單元都可以在棧上建立,函數執行結束時這些存

儲單元自動被釋放。棧記憶體配置運算內建於處理器的指令集中,效率很高,但是分配的記憶體容量有限。

  (3) 從堆上分配,亦稱動態記憶體分配。程式在啟動並執行時候用malloc或new申請任意多少的記憶體,程式員自

己負責在何時用free或delete釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。

   2.記憶體使用量錯誤
      發生記憶體錯誤是件非常麻煩的事情。編譯器不能自動探索這些錯誤,通常是在程式運行時才能捕捉到。

而這些錯誤大多沒有明顯的癥狀,時隱時現,增加了改錯的難度。有時使用者怒氣沖沖地把你找來,程式卻沒有

發生任何問題,你一走,錯誤又發作了。 常見的記憶體錯誤及其對策如下:
       * 記憶體配置未成功,卻使用了它。

  編程新手常犯這種錯誤,因為他們沒有意識到記憶體配置會不成功。常用解決辦法是,在使用記憶體之前檢查

指標是否為NULL。如果是用malloc或new來申請記憶體,應該用if(p==NULL) 或if(p!=NULL)進行防錯處理。

  * 記憶體配置雖然成功,但是尚未初始化就引用它。

  犯這種錯誤主要有兩個起因:一是沒有初始化的觀念;二是誤以為記憶體的預設初值全為零,導致引用初值

錯誤(例如數組)。 記憶體的預設初值究竟是什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不

可信其有。所以無論用何種方式建立數組,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。

  * 記憶體配置成功並且已經初始化,但操作越過了記憶體的邊界。

  例如在使用數組時經常發生下標“多1”或者“少1”的操作。特別是在for迴圈語句中,迴圈次數很容易搞

錯,導致數組操作越界。

  * 忘記了釋放記憶體,造成記憶體泄露。

  含有這種錯誤的函數每被調用一次就丟失一塊記憶體。剛開始時系統的記憶體充足,你看不到錯誤。終有一次

程式突然死掉,系統出現提示:記憶體耗盡。

  動態記憶體的申請與釋放必須配對,程式中malloc與free的使用次數一定要相同,否則肯定有錯誤

(new/delete同理)。

  * 釋放了記憶體卻繼續使用它。
 
  有三種情況:

  (1)程式中的對象調用關係過於複雜,實在難以搞清楚某個對象究竟是否已經釋放了記憶體,此時應該重新

設計資料結構,從根本上解決對象管理的混亂局面。

  (2)函數的return語句寫錯了,注意不要返回指向“棧記憶體”的“指標”或者“引用”,因為該記憶體在函

數體結束時被自動銷毀。

  (3)使用free或delete釋放了記憶體後,沒有將指標設定為NULL。導致產生“野指標”。

  【規則1】用malloc或new申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL的記憶體

  【規則2】不要忘記為數組和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。

  【規則3】避免數組或指標的下標越界,特別要當心發生“多1”或者“少1”操作。

  【規則4】動態記憶體的申請與釋放必須配對,防止記憶體流失。

  【規則5】用free或delete釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。

 
二. 詳解new,malloc,GlobalAlloc
   
 1.  new

  new和delete運算子用於動態分配和撤銷記憶體的運算子

new用法:

          1>     開闢單變數地址空間

               1)new int;  //開闢一個存放數組的儲存空間,返回一個指向該儲存空間的地址.int *a = new

int 即為將一個int類型的地址賦值給整型指標a. 

               2)int *a = new int(5) 作用同上,但是同時將整數賦值為5

          2>    開闢數組空間

               一維: int *a = new int[100];開闢一個大小為100的整型數組空間

         一般用法: new 類型 [初值]

delete用法:

          1> int *a = new int;

               delete a;   //釋放單個int的空間

          2>int *a = new int[5];

               delete [] a; //釋放int數組空間

          要訪問new所開闢的結構體空間,無法直接通過變數名進行,只能通過賦值的指標進行訪問.

          用new和delete可以動態開闢,撤銷地址空間.在編程式時,若用完一個變數(一般是暫時儲存的數組),

下次需要再用,但卻又想省去重新初始化的功夫,可以在每次開始使用時開闢一個空間,在用完後撤銷它.

2.  malloc
  原型:extern void *malloc(unsigned int num_bytes);
  用法:#i nclude <malloc.h>或#i nclude <stdlib.h>
  功能:分配長度為num_bytes位元組的記憶體塊
  說明:如果分配成功則返回指向被分配記憶體的指標,否則返回null 指標NULL。
  當記憶體不再使用時,應使用free()函數將記憶體塊釋放。
  malloc的文法是:指標名=(資料類型*)malloc(長度),(資料類型*)表示指標.
說明:malloc 向系統申請分配指定size個位元組的記憶體空間。傳回型別是 void* 類型。void* 表示未確定類型

的指標。C,C++規定,void* 類型可以強制轉換為任何其它類型的指標。

malloc()函數的工作機制
  malloc函數的實質體現在,它有一個將可用的記憶體塊串連為一個長長的列表的所謂空閑鏈表。調用malloc

函數時,它沿串連表尋找一個大到足以滿足使用者請求所需要的記憶體塊。然後,將該記憶體塊一分為二(一塊的大

小與使用者請求的大小相等,另一塊的大小就是剩下的位元組)。接下來,將分配給使用者的那塊記憶體傳給使用者,並

將剩下的那塊(如果有的話)返回到串連表上。調用free函數時,它將使用者釋放的記憶體塊串連到空閑鏈上。到

最後,空閑鏈會被切成很多的小記憶體片段,如果這時使用者申請一個大的記憶體片段,那麼空閑鏈上可能沒有可以

滿足使用者要求的片段了。於是,malloc函數請求延時,並開始在空閑鏈上翻箱倒櫃地檢查各記憶體片段,對它們

進行整理,將相鄰的小空閑塊合并成較大的記憶體塊。
 
和new的不同
從函式宣告上可以看出。malloc 和 new 至少有兩個不同: new 返回指定類型的指標,並且可以自動計算所需

要大小。比如:
int *p;
p = new int; //傳回型別為int* 類型(整數型指標),分配大小為 sizeof(int);
或:
int* parr;
parr = new int [100]; //傳回型別為 int* 類型(整數型指標),分配大小為 sizeof(int) * 100;
而 malloc 則必須由我們計算要位元組數,並且在返回後強行轉換為實際類型的指標。
int* p;
p = (int *) malloc (sizeof(int));
第一、malloc 函數返回的是 void * 類型,如果你寫成:p = malloc (sizeof(int)); 則程式無法通過編譯,

報錯:“不能將 void* 賦值給 int * 類型變數”。所以必須通過 (int *) 來將強制轉換。
第二、函數的實參為 sizeof(int) ,用於指明一個整型資料需要的大小。如果你寫成:
int* p = (int *) malloc (1);
代碼也能通過編譯,但事實上只分配了1個位元組大小的記憶體空間,當你往裡頭存入一個整數,就會有3個位元組無

家可歸,而直接“住進鄰居家”!造成的結果是後面的記憶體中原有資料內容全部被清空。

3.  GlobalAlloc
 
   VC中關於GlobalAlloc,GlobalLock,GlobalUnLock

調用GlobalAlloc函數分配一塊記憶體,該函數會返回分配的記憶體控制代碼。
調用GlobalLock函數鎖定記憶體塊,該函數接受一個記憶體控制代碼作為參數,然後返回一個指向被鎖定的記憶體塊的指

針。 您可以用該指標來讀寫記憶體。
調用GlobalUnlock函數來解鎖先前被鎖定的記憶體,該函數使得指向記憶體塊的指標無效。
調用GlobalFree函數來釋放記憶體塊。您必須傳給該函數一個記憶體控制代碼。
 
GlobalAlloc
說明
分配一個全域記憶體塊
傳回值
Long,返回全域記憶體控制代碼。零表示失敗。會設定GetLastError
參數表
參數 類型及說明
wFlags Long,對分配的記憶體類型進行定義的常數標誌,如下所示:
             GMEM_FIXED 分配一個固定記憶體塊
             GMEM_MOVEABLE 分配一個可移動記憶體塊
             GMEM_DISCARDABLE 分配一個可丟棄記憶體塊
             GMEM_NOCOMPACT 堆在這個函數調用期間不進行累積
             GMEM_NODISCARD 函數調用期間不丟棄任何記憶體塊
             GMEM_ZEROINIT 新分配的記憶體塊全部初始化成零
dwBytes Long,要分配的字元數

  GlobalLock 
函數功能描述:鎖定一個全域的記憶體對象,返回指向該對象的第一個位元組的指標
函數原型:
LPVOID GlobalLock( HGLOBAL hMem )
參數:
hMem:全域記憶體對象的控制代碼。這個控制代碼是通過GlobalAlloc或GlobalReAlloc來得到的
傳回值:
調用成功,返回指向該對象的第一個位元組的指標
調用失敗,返回NULL,可以用GetLastError來獲得出錯資訊
注意:
調用過GlobalLock鎖定一塊記憶體區後,一定要調用GlobalUnlock來解鎖
 
  GlobalUnlock
函數功能描述:解除被鎖定的全域記憶體對象
函數原型:BOOL GlobalUnlock( HGLOBAL hMem );
參數:hMem:全域記憶體對象的控制代碼
傳回值:
非零值,指定的記憶體對象仍處於被鎖定狀態
0,函數執行出錯,可以用GetLastError來獲得出錯資訊,如果返回NO_ERROR,則表示記憶體對象已經解鎖了
注意:    這個函數實際上是將記憶體對象的鎖定計數器減一,如果計數器不為0,則表示執行過多個GlobalLock

函數來對這個記憶體對象加鎖,需要對應數目的GlobalUnlock函數來解鎖。如果通過GetLastError函數返回錯誤

碼為ERROR_NOT_LOCKED,則表示未加鎖或已經解鎖。

  樣本:
// Malloc memory
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSize);
// Lock memory
pMem = (BYTE *) GlobalLock(hMem);
..................
// Unlock memory
GlobalUnlock(hMem);
GlobalFree(hMem);

三 總結

靈活自由是C/C++語言的一大特色,而這也為C/C++程式員出了一個難題。當程式越來越複雜時,記憶體的管理也

會變得越加複雜,稍有不慎就會出現記憶體問 題。記憶體流失是最常見的記憶體問題之一。記憶體流失如果不是很嚴重

,在短時間內對程式不會有太大的影響,這也使得記憶體流失問題有很強的隱蔽性,不容易被發現。 然而不管內

存泄漏多麼輕微,當程式長時間運行時,其破壞力是驚人的,從效能下降到記憶體耗盡,甚至會影響到其他程式

的正常運行。另外記憶體問題的一個共同特點 是,記憶體問題本身並不會有很明顯的現象,當有異常現象出現時已

時過境遷,其現場已非出現問題時的現場了,這給調試記憶體問題帶來了很大的難度。

 下載Windows Debug 工具, http://www.microsoft.com/whdc/devtools/debugging/default.mspx
安裝後,使用其中的gflags.exe工具開啟PageHeap,
gflags -p /enable MainD.exe /full
重新使用VS用調試方式運行,很快就找到了出錯位置,因為在某個靜態函數中筆誤導致

在編寫穩定的伺服器程式時,這個工具尤為有用。

參考文獻及網頁地址:
1. http://www.bccn.net/Article/kfyy/cjj/jszl/200607/4172.html
2. http://www.7880.com/Info/Article-8282a500.html
3. http://www.cnblogs.com/jjzhou1988/archive/2008/11/30/1344314.html
4. http://blog.chinaunix.net/u3/101356/showart_2031203.html
5. http://www.cnblogs.com/howareyou586/archive/2008/11/06/1328353.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.