1. 程式人生 > >記憶體分配:malloc, new, HeapAlloc, VirtualAlloc

記憶體分配:malloc, new, HeapAlloc, VirtualAlloc

VC程式設計精粹需要進行記憶體的動態分配和釋放操作,本文總結常用的VC對記憶體的操作方法並比較他們之間的區別,以便於讀者能夠加深對他們的理解並根據專案的實際情況選用適合自己的方案。

1、GlobalAlloc()
The GlobalAlloc function allocates the specified number of bytes from the heap. Windows memory management does not provide a separate local heap and global heap. 
Note   The global functions are slower than other memory management functions and do not provide as many features. Therefore, new applications should use the heap functions. However, the global functions are still used with DDE, the clipboard functions, and OLE data objects.
GlobalAlloc()主要用於Win32應用程式實現從全域性堆中分配出記憶體供程式使用,是16位WINDOWS程式使用的API,對應於系統的全域性棧,返回一個記憶體控制代碼,在實際需要使用時,用GlobalLock()來實際得到記憶體 區。但32位WINDOWS系統中全域性棧和區域性堆的區別已經不存在了,因此不推薦在Win32中使用該函式,應使用新的記憶體分配函式HeapAlloc()以得到更好的支援,GlobalAlloc()還可以用,主要是為了 相容。
函式原型為:
HGLOBAL GlobalAlloc(
UINT uFlags,
SIZE_T dwBytes
);
uFlags引數含義:
GHND   GMEM_MOVEABLE和GMEM_ZEROINIT的組合
GMEM_FIXED   分配固定記憶體,返回值是一個指標
GMEM_MOVEABLE   分配活動記憶體,在Win32中,記憶體塊不能在實體記憶體中移動,但能在預設的堆中移動。返回值是記憶體物件的控制代碼,用函式GlobalLock可將控制代碼轉化為指標
GMEM_ZEROINIT   將記憶體內容初始化為零
GPTR   GMEM_FIXED和GMEM_ZEROINIT的組合
一般情況下我們在程式設計的時候,給應用程式分配的記憶體都是可以移動的或者是可以丟棄的,這樣能使有限的記憶體資源充分利用,所以,在某一個時候我們分配的那塊 記憶體的地址是不確定的,因為他是可以移動的,所以得先鎖定那塊記憶體塊,這兒應用程式需要呼叫API函式GlobalLock函式來鎖定控制代碼。如下: lpMem=GlobalLock(hMem); 這樣應用程式才能存取這塊記憶體。所以我們在使用GlobalAllock時,通常搭配使用GlobalLock,當然在不使用記憶體時,一定記得使用 GlobalUnlock,否則被鎖定的記憶體塊一直不能被其他變數使用。
GlobalAlloc對應的釋放空間的函式為GlobalFree。
GlobalAlloc是win16留下來的函式,它呼叫HeapAlloc分配堆中的記憶體。在理想的win32環境下,我們不需要 GlobalAlloc,但是實際上,我們還得保留從win16移植過來的許多程式碼。在這些程式碼中使用了“記憶體控制代碼”(HGLOBAL)引數而不是32位 的記憶體地址。
GlobalAlloc根據其屬性引數做兩件不同的事情。如果引數指定了GMEM_FIXED,則GlobalAlloc簡單呼叫HeapAlloc,把 返回地址作為一個32位HGLOBAL值;如果引數指定了GMEM_MOVEABLE,則返回的HGLOBAL值是一個指向程序裡控制代碼表中某一項入口的指 針,該入口包含指向實際HeapAlloc分配的記憶體的指標。
從本質上,如果我們不呼叫GlobalReAlloc函式,我們就可以用HeapAlloc代替GlobalAlloc。

2、HeapAlloc()
The HeapAlloc function allocates a block of memory from a heap.The allocated memory is not movable.
MSDN上的解釋為:HeapALloc是從堆上分配一塊記憶體,且分配的記憶體是不可移動的(即如果沒有連續的空間能滿足分配的大小,程式不能將其他零散的 空間利用起來,從而導致分配失敗),該分配方法是從一指定地址開始分配,而不像GloabalAlloc是從全域性堆上分配,這個有可能是全域性,也有可能是 區域性。函式原型為:
LPVOID
HeapAlloc(
    HANDLE hHeap,
    DWORD dwFlags,
   SIZE_T dwBytes
    );
hHeap是程序堆記憶體開始位置。
dwFlags是分配堆記憶體的標誌。包括HEAP_ZERO_MEMORY,即使分配的空間清零。
dwBytes是分配堆記憶體的大小。
其對應的釋放空間函式為HeapFree。
使用HeapAlloc分配的記憶體有兩點和GlobalAlloc不同:
1)、不可以移動,所以可能會產生記憶體碎片
2)、可以提供序列化訪問功能,多執行緒時不用自己再同步了

3、malloc()
malloc()是C執行庫中的動態記憶體分配函式,主要用於ANSI C程式中,是標準庫函式。WINDOWS程式基本不再使用這種方法進行記憶體操作,因為它比WINDOWS記憶體分配函式少了一些特性,如整理記憶體。
原型:extern void *malloc(unsigned int num_bytes);
標頭檔案:#include   <memory.h>
功能:分配長度為num_bytes位元組的記憶體塊
說明:如果分配成功則返回指向被分配記憶體的指標,否則返回空指標NULL。當記憶體不再使用時,應使用free()函式將記憶體塊釋放。
舉例:

// malloc.c
#include   <memory.h>
#include   <stdio.h>
main()
{
    char *p;
    clrscr(); // clear screen
    p=(char *)malloc(100);
    if(p)
        printf("Memory Allocated at: %x",p);
    else
        printf("Not Enough Memory!\n");
    free(p);
    getchar();
    return 0;
}

4、new
標準C++一般使用new語句分配動態的記憶體空間,需要申請陣列時,可以直接使用new int[3]這樣的方式,釋放該方法申請的記憶體空間使用對應的delete語句,需要釋放的記憶體空間為一個數組,則使用delete [] ary;這樣的方式。
要訪問new所開闢的結構體空間,無法直接通過變數名進行,只能通過賦值的指標進行訪問.
new在內部呼叫malloc來分配記憶體,delete在內部呼叫free來釋放記憶體。
舉例:

#include
#include
using namespace std;
int main() {
    int *a = new int[34];
    int *b = new int[];
    int (*c)[2] = new int[34][2];
    int (*d)[2] = new int[][2];
    int (*e)[2][3] = new int[34][2][3];
    int (*f)[2][3] = new int[][2][3];
    a[0] = 1;
    b[0] = 1; //執行時錯誤,無分配的記憶體,b只起指標的作用,用來指向相應的資料
    c[0][0] = 1;
    d[0][0] = 1; //執行時錯誤,無分配的記憶體,d只起指標的作用,用來指向相應的資料
    e[0][0][0] = 1;
    f[0][0][0] = 1;   //執行時錯誤,無分配的記憶體,f只起指標的作用,用來指向相應的資料
    cout<< cout<< cout<< cout<< cout<< cout<< delete[] a; delete[] b; delete[] c;
    delete[] d; delete[] e; delete[] f;
}

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

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();效率稍稍高一些。