在C語言中只能通過malloc()和其派生的函數進行動態的申請內存,而實現的根本是通過系統調用實現的(在linux下是通過sbrk()系統調用實現)。
malloc()到底從哪里得到了內存空間?答案是從堆里面獲得空間。也就是說函數返回的指針是指向堆里面的一塊內存。操作系統中有一個記錄空閑內存地址的鏈表。當操作系統收到程序的申請時,就會遍歷該鏈表,然后就尋找第一個空間大於所申請空間的堆結點,然后就將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序。
malloc()在運行期動態分配分配內存,free()釋放由其分配的內存。malloc()在分配用戶傳入的大小的時候,還分配的一個相關的用於管理的額外內存,不過,用戶是看不到的。所以,
實際的大小 = 管理空間 + 用戶空間
在64位系統中,malloc(0)的有效內存大小為24,32位中為12,准確的說是至少是這么多,並且這些內存是可以用的
結果為
此外,堆中的內存塊總是成塊分配的,並不是申請多少字節,就拿出多少個字節的內存來提供使用。堆中內存塊的大小通常與內存對齊有關(8Byte(for 32bit system)或16Byte(for 64bit system)。
因此,在64位系統下,當(申請內存大小+sizeof(struct mem_control_block) )% 16 == 0的時候,剛好可以完成一次滿額的分配,但是當其!=0的時候,就會多分配內存塊。
在linux系統下面一個程序的堆的管理是通過內存塊進行管理的,也就是將堆分成了很多大小不一的內存塊。這些塊怎么管理尼,比如怎么查詢塊的大小,怎么查詢塊是否正在被程序使用,怎么知道這個塊的地址。為了解決內存塊的管理所以要設計一個管理內存塊的數據結構,詳細的數據結構如下:
/**內存控制塊數據結構,用於管理所有的內存塊 * is_available: 標志着該塊是否可用。1表示可用,0表示不可用 * size: 該塊的大小 **/
struct mem_control_block { int is_available; int size; };
有了管理內存塊的數據結構,那么在內存中堆的組織形式也好理解了,也就是堆是由很多內存塊組成的,所以有了如下的示意圖:

綜合上面的知識,可以很容易想到malloc()實現的大體思路。首先挨個檢查堆中的內存是否可用,如果可用那么大小是否能滿足需求,要是都滿足的話就直接用。當遍歷了堆中的所有內存塊時,要是沒有能滿足需求的塊時就只能通過系統調用向操作系統申請新的內存,然后將新的內存添加到堆中。思路很簡單,malloc()實現流程圖如下所示:

看完上面的思路,也會很容易的想到free()函數的實現思路,只要將內存管理塊設置為可用就可以了。這樣下次調用malloc()函數的時候就可以將該內存塊作為可分配塊再次進行分配了。
最后,貼上malloc()和free()實現的代碼:
malloc()實現:
/**內存控制塊數據結構,用於管理所有的內存塊 * is_available: 標志着該塊是否可用。1表示可用,0表示不可用 * size: 該塊的大小 **/ struct mem_control_block { int is_available; int size; }; /**在實現malloc時要用到linux下的全局變量 *managed_memory_start:該指針指向進程的堆底,也就是堆中的第一個內存塊 *last_valid_address:該指針指向進程的堆頂,也就是堆中最后一個內存塊的末地址 **/ void *managed_memory_start; void *last_valid_address; /**malloc()功能是動態的分配一塊滿足參數要求的內存塊 *numbytes:該參數表明要申請多大的內存空間 *返回值:函數執行結束后將返回滿足參數要求的內存塊首地址,要是沒有分配成功則返回NULL **/ void *malloc(size_t numbytes) { //游標,指向當前的內存塊 void *current_location; //保存當前內存塊的內存控制結構 struct mem_control_block *current_location_mcb; //保存滿足條件的內存塊的地址用於函數返回 void *memory_location; memory_location = NULL; //計算內存塊的實際大小,也就是函數參數指定的大小+內存控制塊的大小 numbytes = numbytes + sizeof(struct mem_control_block); //利用全局變量得到堆中的第一個內存塊的地址 current_location = managed_memory_start; //對堆中的內存塊進行遍歷,找合適的內存塊 while (current_location != last_valid_address) //檢查是否遍歷到堆頂了 { //取得當前內存塊的內存控制結構 current_location_mcb = (struct mem_control_block*)current_location; //判斷該塊是否可用 if (current_location_mcb->is_available) //檢查該塊大小是否滿足 if (current_location_mcb->size >= numbytes) { //滿足的塊將其標志為不可用 current_location_mcb->is_available = 0; //得到該塊的地址,結束遍歷 memory_location = current_location; break; } //取得下一個內存塊 current_location = current_location + current_location_mcb->size; } //在堆中已有的內存塊中沒有找到滿足條件的內存塊時執行下面的函數 if (!memory_location) { //向操作系統申請新的內存塊 if (sbrk(numbytes) == -1) return NULL;//申請失敗,說明系統沒有可用內存 memory_location = last_valid_address; last_valid_address = last_valid_address + numbytes; current_location_mcb = (struct mem_control_block)memory_location; current_location_mcb->is_available = 0; current_location_mcb->size = numbytes; } //到此已經得到所要的內存塊,現在要做的是越過內存控制塊返回內存塊的首地址 memory_location = memory_location + sizeof(struct mem_control_block); return memory_location; }
free()實現:
/**free()功能是將參數指向的內存塊進行釋放 *firstbyte:要釋放的內存塊首地址 *返回值:空 **/
void free(void *firstbyte) { struct mem_control_block *mcb; //取得該塊的內存控制塊的首地址
mcb = firstbyte - sizeof(struct mem_control_block); //將該塊標志設為可用
mcb->is_available = 1; return; }
參考:
https://blog.csdn.net/c1s2p3/article/details/50522185
https://blog.csdn.net/qq_29350001/article/details/70213602
https://www.cnblogs.com/debuging/p/3158147.html