C 語言記憶體分配函式
1、ANSI C 中的記憶體空間分配函式
ANSI C 中有 3 個分配記憶體的函式:malloc,calloc,realloc。
函式原型:
#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
簡要說明: 1.malloc 分配長度為 size 個位元組的空間,返回指向該空間的空型別指標,空間中的初始內容不確定。 2.calloc 分配長度為 nmemb*size 位元組的空間,返回指向空間的指標,空間中的的位都被初始化為 0。 3.realloc 改變 ptr 指向的記憶體塊的大小,將其調整為 size 個位元組。其中的內容,從開始到 min(old size, new size) 的位置都保持不變(可能通過拷貝實現)。如果容量被擴大,新增加的空間不會初始化。 注意:malloc,calloc,realloc函式返回一個void型指標 ,指向重新分配大小的記憶體。如果請求失敗,則返回 NULL。 4.free 釋放之前呼叫 calloc、malloc 或 realloc 所分配的記憶體空間。free的引數必須要麼是NULL,要麼是從malloc、relloc、calloc返回的值。
例如:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *str = NULL;
/* 最初的記憶體分配 */
str = (char *) malloc(15);
strcpy(str, "runoob");
printf("String = %s, Address = %s\n", str, str);
/* 重新分配記憶體 */
str = (char *) realloc(str, 25);
strcat (str, ".com");
printf("String = %s, Address = %s\n", str, str);
/* 釋放已分配的記憶體 */
free(str);
str = NULL; //free之後,需要將str置為NULL,否則就會成為野指標
//避免多次free,否則要麼程式崩潰,要麼出現重大隱患
return(0);
}
為了避免free之後,記憶體指標被再次free或使用,可定義如下巨集:
#ifndef SAFE_FREE
#define SAFE_FREE(x) do { if ((x) != NULL) { free((x)); (x) = NULL;} } while(0) ///< 安全釋放動態分配的記憶體
#endif
1.2、細說realloc函式
realloc函式嘗試重新調整之前呼叫 malloc 或 calloc 所分配的 ptr 所指向的記憶體塊的大小。
引數
- ptr – 指標指向一個要重新分配記憶體的記憶體塊,該記憶體塊之前是通過呼叫 malloc、calloc 或 realloc 進行分配記憶體的。如果為空指標,則會分配一個新的記憶體塊,且函式返回一個指向它的指標。
- size – 記憶體塊的新的大小,以位元組為單位。如果大小為 0,且 ptr 指向一個已存在的記憶體塊,則 ptr 所指向的記憶體塊會被釋放,並返回一個空指標。
返回值 該函式返回一個指標 ,指向重新分配大小的記憶體。如果請求失敗,則返回 NULL。
注意:realloc函式用於修改一個原先已經分配的記憶體塊的大小,可以使一塊記憶體的擴大或縮小。當起始空間的地址為空,即ptr = NULL,則同malloc。當ptr非空:若nuw_size < size,即縮小ptr所指向的記憶體空間,該記憶體塊尾部的部分記憶體被拿掉,剩餘部分記憶體的原先內容依然保留;若nuw_size > size,即擴大ptr所指向的記憶體空間,如果原先的記憶體尾部有足夠的擴大空間,則直接在原先的記憶體塊尾部新增記憶體,如果原先的記憶體尾部空間不足,或原先的記憶體塊無法改變大小,realloc將重新分配另一塊nuw_size大小的記憶體,並把原先那塊記憶體的內容複製到新的記憶體塊上。因此,使用realloc後就應該改用realloc返回的新指標。
2、記憶體分配方式
- 從靜態儲存區域分配. 記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在.例如全域性變數、static變數.
- 在棧上建立 在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放.棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限.
- 從堆上分配,亦稱動態記憶體分配. 程式在執行的時候用malloc或new申請任意多少的記憶體,程式設計師自己負責在何時用free或delete釋放
3、stdlib.h與malloc.h的區別
使用動態記憶體分配函式時,實際上,許多編譯系統實現時,往往增加了一些其他函式。ANSI標準建議在“stdlib.h”標頭檔案中包含有相關的資訊,但大多數C編譯系統要求用“mallco.h”而不是“stdlib.h"。一般來說stdlib.h包含malloc.h。
4、alloca函式
除了上面 3 個最常見的函式外,還有一個 alloca 函式值得一提。它與 malloc 功能上幾乎一致,不同之處在於其在棧上分配空間,而不是在堆上分配空間。正因為如此,分配的空間在函式呼叫結束後會自動釋放。另一方面,在某些系統中棧在函式呼叫期間可能無法改變大小,這樣的系統中不能提供對 alloca 的支援。同時我認為,如果棧幀的大小可改變的範圍受到嚴格限制,alloca 函式在分配較大空間時很容易引起棧溢位異常。
#include <alloca.h>
void *alloca(size_t size);
alloca 是它在 Linux 中的名字,在 windows 中應該叫 _alloca,而且包含在標頭檔案中,可以參考 MSDN 中的說明。
5、glibc 中的 memalign 函式
除了 ANSI C 中規定的記憶體分配函式,glibc 額外提供了地址對齊的記憶體空間的分配函式。主要有這麼幾個:posix_memalign, aligned_alloc, valloc, memalign, pvalloc。
函式原型:
#include <stdlib.h>
int posix_memalign(void **memptr, size_t alignment, size_t size);
void *aligned_alloc(size_t alignment, size_t size);
void *valloc(size_t size);
#include <malloc.h>
void *memalign(size_t alignment, size_t size);
void *pvalloc(size_t size);
簡要說明
- posix_memalign 分配 size 位元組的記憶體空間,將 *memptr 指向分配的空間,並且空間的地址是 alignment 的倍數。alignment 必須是 2 的乘冪,同時也要是 sizeof(void *) 的倍數。
- aligned_alloc 分配 size 位元組的空間,返回指向該空間的指標。除了地址是 alignment 的倍數這一限制和 alignment 必須是 2 的乘冪的限制外,size 也必須是 alignment 的倍數。
- valloc 分配 size 位元組的空間,返回指向該空間的指標,空間的地址是頁大小的倍數。該函式可能已經過時,不提倡使用。
- memalign 分配 size 位元組的空間,返回指向該空間的指標,空間的地址是 alignment 的倍數,alignment 必須是 2 的乘冪。該函式可能已經過時,不提倡使用。
- pvalloc 與 valloc 相似,不過將分配的空間大小擴充套件為頁大小的倍數。該函式可能已經過時,不提倡使用。
上述對齊分配函式都不會把分配的空間置 0 ,需手動初始化。
在GNU系統中,malloc或realloc返回的記憶體塊地址都是8的倍數(如果是64位系統,則為16的倍數)。如果你需要更大的粒度,請使用memalign或valloc。
6、效能測試
為了觀察不同記憶體分配函式的效能,我在 Linux 系統上測試了幾個函式。測試的環境為 3.13.0-35 版本核心,編譯器 gcc 4.8.2,glibc 為 libc-2.19.so。