詳解C標準庫堆記憶體函式

2022-09-27 05:21:09 字數 2802 閱讀 5634

c標準庫堆記憶體函式有4個:malloc、free、calloc、realloc,其函式宣告放在了#include 中,主要用來申請和釋放堆記憶體。

堆記憶體的申請和釋放(wiki,chs),需要發起系統呼叫,會帶來昂貴的上下文切換(使用者態切換到核心態),十分耗時。另外,這些過程可能是帶鎖的,難以並行化。

對於作業系統而言,記憶體管理的基本單位是頁(通常為4k),而不是需要4 bytes時,就給你分配4 bytes,釋放4 bytes時,就給你釋放4 bytes。

因此,為了提公升效率,作業系統會呼叫系統api(windows上是v其他平台是mmap、munmap)來實現乙個ansi c記憶體分配器(工作在使用者態),供c標準庫堆記憶體函式來使用。

(1)記憶體碎片(fragmentation):即空閒記憶體不能被利用。分為外部碎片(在分配單元間的未使用的記憶體);內部碎片(在分配單元中未使用的記憶體)

(2)記憶體碎片的罪魁禍首就是小塊記憶體的頻繁分配

(3)記憶體碎片無法避免,只能通過記憶體分配器演算法來減少,例如:接近大小的記憶體就近分配,釋放時能合併就合併,從而減少碎片

(4)上面講的記憶體碎片指的是虛擬記憶體碎片,os是不管的,os只管物理記憶體。

(5)平時我們說的記憶體碎片整理(defragment)或記憶體緊縮(memory compaction),是指os對物理記憶體進行的碎片整理,把分開小的物理記憶體頁移動在一起形成乙個大的整塊。

os整理完物理記憶體後,會用新的物理記憶體位址來更新虛擬記憶體與物理記憶體對映表,這些對於上層邏輯都是透明的。

虛擬記憶體是不能進行碎片整理的,主要原因是碎片整理會移動記憶體,上層邏輯的指標位址確還是指向老的位址,這會導致致命錯誤。

(1)分配和釋放的效率

(2)記憶體分配器的利用率。包括以下幾個方面:

① 記憶體對齊導致的不可使用的記憶體碎片(內部碎片)

② 記憶體碎片太嚴重,使得分配大塊記憶體時,找不到空閒塊,最後導致記憶體分配失敗(外部碎片)

③ 記憶體頁始終有被使用,導致分配器無法及時釋放該頁的記憶體占用,使得整個記憶體分配器的記憶體占用被撐得很大,縮不回去

形參size為要求分配的位元組數。如果函式執行成功,malloc返回獲得記憶體空間的首位址;如果函式執行失敗,那麼返回ibxifx值為null。

由於 malloc函式值的型別為void型指標,因此,可以將其值型別轉換後賦給任意型別指標,這樣就可以通過操作該型別指標來操作從堆上獲得的記憶體空間。

需要注意的是,malloc函式分配得到www.cppcns.com的記憶體空程式設計客棧間是未初始化的。可通過呼叫memset來將其初始化為全0。

int* p = (int *) malloc(sizeof(int)*100);

if (p == null)

memset(p, 0, sizeof(int)*100);

從堆上獲得的記憶體,在程式結束之前,系統不會將其自動釋放,需要程式設計師來自己管理,防止出現記憶體洩露。

free(p);

p = null;

calloc函式的功能與malloc函式的功能相似,都是從堆分配記憶體。

函式返回值為void*。如果執行成功,從堆上獲得size * num大小的堆記憶體,並返回該記憶體塊的首位址。如果執行失敗,函式返回null。

與malloc函式不同的是,calloc函式得到的記憶體塊會被初始化為全0。由於提供了2個引數,比較適合為陣列申請空間,可以將size設定為陣列元素的空間長度,將num設定為陣列的容量。

int* p = (int *) calloc(100, sizeof(int));

if (p == null)

為ptr重新分配大小為size的一塊記憶體空間。下面是這個函式的工作流程:

① 如果ptr為null,則函式相當於malloc(new_size),試著分配一塊大小為new_size的記憶體,如果成功將位址返回,否則返回null。

② 如果ptr不為null,檢視ptr是不是在堆中,如果不是的話會丟擲realloc invalid pointer異常。如果ptr在堆中,則檢視new_size大小。

(a)如果new_size大小為0,則相當於free(ptr),將ptr指向的記憶體空間釋放掉,返回null。

(b)如果new_size小於原大小,只有new_size大小的資料會儲存,後面位址的資料可能會丟失;

(c)如果new_size等於原大小,什麼都沒有做;

(d)如果new_size大於原大小,則檢視ptr指向的位置還有沒有足夠的連續記憶體空間,如果有的話,分配更多的空間,返回的位址和ptr相同;

如果沒有的話,在更大的空間內查詢,如果找到new_size大小的空間,將舊的內容拷貝到新的記憶體中,把舊的記憶體釋放掉,則返回新位址,否則返回null。

int* p = (int*)malloc(sizeof(int));

*p = 3;

printf("p=%p\n", p); // p=0000020b2966e310

printf("*p=%d\n", *p); // *p=3

p = (int*)realloc(p, sizeof(int)); // 什麼也不做

printf("p=%p\n", p); // p=0000020b2966e310

printf("*p=%d\n", *p); // *p=3

p = (int*)realloc(p, 1024 * sizeof(int)); // 建立4kb的記憶體塊 注:4kb為乙個頁面的大小

printf("p=%p\n", p); // p=0000020b29673a50 注:由於不能在原來位址上擴容,會將原來位址記憶體釋放,並在新位址申請記憶體塊

printf("*p=%d\n", *p); // *p=3

realloc(p, 0); // 相當於free(p)

p = null;

C 堆記憶體和棧記憶體詳解

堆 順序隨意 棧 先進後出 堆和棧的區別 一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧 2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不...

C 堆記憶體和棧記憶體詳解

堆 順序隨意 棧 先進後出 堆和棧的區別 一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧 2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不...

C 堆記憶體和棧記憶體詳解

乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放 存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧 2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結構中的堆是兩回事,分配方式倒是類...