1. 程式人生 > >C 語言記憶體分配函式

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、記憶體分配方式

  1. 從靜態儲存區域分配. 記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在.例如全域性變數、static變數.
  2. 在棧上建立 在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放.棧記憶體分配運算內置於處理器的指令集中,效率很高,但是分配的記憶體容量有限.
  3. 從堆上分配,亦稱動態記憶體分配. 程式在執行的時候用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);

簡要說明

  1. posix_memalign 分配 size 位元組的記憶體空間,將 *memptr 指向分配的空間,並且空間的地址是 alignment 的倍數。alignment 必須是 2 的乘冪,同時也要是 sizeof(void *) 的倍數。
  2. aligned_alloc 分配 size 位元組的空間,返回指向該空間的指標。除了地址是 alignment 的倍數這一限制和 alignment 必須是 2 的乘冪的限制外,size 也必須是 alignment 的倍數。
  3. valloc 分配 size 位元組的空間,返回指向該空間的指標,空間的地址是頁大小的倍數。該函式可能已經過時,不提倡使用。
  4. memalign 分配 size 位元組的空間,返回指向該空間的指標,空間的地址是 alignment 的倍數,alignment 必須是 2 的乘冪。該函式可能已經過時,不提倡使用。
  5. 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