內存分配詳解 malloc, new, HeapAlloc, VirtualAlloc,GlobalAlloc


  很多地方都會使用內存,內存使用過程中操作不當就容易崩潰,無法運行程序,上網Google學習一下,了解整理下他們之間的區別以及使用 ,獲益匪淺

0x01 各自的定義和理解

   (1)先看GlobalAlloc()

    GlobalAlloc()主要用於Win32應用程序實現從全局堆中分配出內存供2017-03-05程序使用,是16位WINDOWS程序使用的API,對應於系統的全局棧,返回一個內存句柄,在實際需要使用時,用GlobalLock()來實際得到內存 區。但32位WINDOWS系統中全局棧和局部堆的區別已經不存在了,因此不推薦在Win32中使用該函數,應使用新的內存分配函數HeapAlloc()以得到更好的支持,GlobalAlloc()還可以用,主要是為了 兼容。

    一般情況下我們在編程的時候,給應用程序分配的內存都是可以移動的或者是可以丟棄的,這樣能使有限的內存資源充分利用,所以,在某一個時候我們分配的那塊 內存的地址是不確定的,因為他是可以移動的,所以得先鎖定那塊內存塊,這兒應用程序需要調用API函數GlobalLock函數來鎖定句柄。如下: lpMem=GlobalLock(hMem); 這樣應用程序才能存取這塊內存。所以我們在使用GlobalAllock時,通常搭配使用GlobalLock,當然在不使用內存時,一定記得使用 GlobalUnlock,否則被鎖定的內存塊一直不能被其他變量使用。

    GlobalAlloc對應的釋放空間的函數為GlobalFree。

  (2)HeapAlloc()

    HeapALloc是從堆上分配一塊內存,且分配的內存是不可移動的(即如果沒有連續的空間能滿足分配的大小,程序不能將其他零散的 空間利用起來,從而導致分配失敗),該分配方法是從一指定地址開始分配,而不像GloabalAlloc是從全局堆上分配,這個有可能是全局,也有可能是 局部

  (3)malloc()

  是C運行庫中的動態內存分配函數,主要用於ANSI C程序中,是標准庫函數。WINDOWS程序基本不再使用這種方法進行內存操作,因為它比WINDOWS內存分配函數少了一些特性,如整理內存

  (4)new

標准C++一般使用new語句分配動態的內存空間,需要申請數組時,可以直接使用new int[3]這樣的方式,釋放該方法申請的內存空間使用對應的delete語句,需要釋放的內存空間為一個數組,則使用delete [] ary;這樣的方式。

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

new在內部調用malloc來分配內存,delete在內部調用free來釋放內存。

  (5)

(1) VirtualAlloc  下面是網友的解釋 但我個人的理解這個才是內存申請的鼻祖,所有的內存的申請都感覺默認調用了它

    PVOID VirtualAlloc(PVOID pvAddress, SIZE_T dwSize, DWORD fdwAllocationType, DWORD fdwProtect)

VirtualAlloc是Windows提供的API,通常用來分配大塊的內存。例如如果想在進程A和進程B之間通過共享內存的方式實現通信,可以使用該函數(這也是較常用的情況)。不要用該函數實現通常情況的內存分配。該函數的一個重要特性是可以預定指定地址和大小的虛擬內存空間。例如,希望在進程的地址空間中第50MB的地方分配內存,那么將參數 50*1024*`1024 = 52428800 傳遞給pvAddress,將需要的內存大小傳遞給dwSize。如果系統有足夠大的閑置區域能滿足請求,則系統會將該塊區域預訂下來並返回預訂內存的基地址,否則返回NULL。

使用VirtualAlloc分配的內存需要使用VirtualFree來釋放。

  0x02 區別與聯系

它們之間的區別主要有以下幾點:

1、GlobalAlloc()函數在程序的堆中分配一定的內存,是Win16的函數,對應於系統的全局棧,而在Win32中全局棧和局部堆的區別已經不存在了,因此不推薦在Win32中使用該函數。

2、malloc()是標准庫函數,而new則是運算符,它們都可以用於申請動態內存。

3、new()實際上調用的是malloc()函數。

4、new運算符除了分配內存,還可以調用構造函數,但是malloc()函數只負責分配內存。

5、對於非內部數據類型的對象而言,只使用malloc()函數將無法滿足動態對象的要求,因為malloc()函數不能完成執行構造函數的任務。

6、malloc(); 和 HeapAlloc(); 都是從堆中分配相應的內存,不同的是一個是c run time的函數,一個是windows系統的函數, 對於windows程序來說,使用HeapAlloc();會比malloc();效率稍稍高一些。

  0x03關於內存的初始化和使用

    

    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,防止產生“野指針”。

 

 

這些都是我查閱資料的整理 希望會有所幫助


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM