new与malloc的区别以及实现方法
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 https://blog.csdn.net/shanghairuoxiao/article/details/70337890
new和malloc的內(nèi)存分配在哪
分配在堆上。也有說new是分配在自由存儲區(qū)而malloc分配在堆上,自由存儲區(qū)可以是堆也可以不是,具體要看new內(nèi)部的實(shí)現(xiàn)。操作系統(tǒng)在堆上維護(hù)一個空閑內(nèi)存鏈表,當(dāng)需要分配內(nèi)存的時候,就查找這個表,找到一塊內(nèi)存大于所需內(nèi)存的區(qū)域,分配內(nèi)存并將剩余的內(nèi)存空間返還到空閑鏈表上(如果有剩余的話)。
new/delete和malloc/free的區(qū)別
1. malloc和free是庫函數(shù),而new和delete是C++操作符;
2. new自己計(jì)算需要的空間大小,比如’int * a = new,malloc需要指定大小,例如’int * a = malloc(sizeof(int))’;
3. new在動態(tài)分配內(nèi)存的時候可以初始化對象,調(diào)用其構(gòu)造函數(shù),delete在釋放內(nèi)存時調(diào)用對象的析構(gòu)函數(shù)。而malloc只分配一段給定大小的內(nèi)存,并返回該內(nèi)存首地址指針,如果失敗,返回NULL。
4. new是C++操作符,是關(guān)鍵字,而operate new是C++庫函數(shù)
5. opeartor new /operator delete可以重載,而malloc不行
6. new可以調(diào)用malloc來實(shí)現(xiàn),但是malloc不能調(diào)用new來實(shí)現(xiàn)
7. 對于數(shù)據(jù)C++定義new[]專門進(jìn)行動態(tài)數(shù)組分配,用delete[]進(jìn)行銷毀。new[]會一次分配內(nèi)存,然后多次調(diào)用構(gòu)造函數(shù);delete[]會先多次調(diào)用析構(gòu)函數(shù),然后一次性釋放。
分配數(shù)組不同之處 int char* pa = new char[100]; int char* pb = malloc(sizeof(char) * 100);- 1
- 2
- 3
8. malloc能夠直觀地重新分配內(nèi)存
使用malloc分配的內(nèi)存后,如果在使用過程中發(fā)現(xiàn)內(nèi)存不足,可以使用realloc函數(shù)進(jìn)行內(nèi)存重新分配實(shí)現(xiàn)內(nèi)存的擴(kuò)充。realloc先判斷當(dāng)前的指針?biāo)竷?nèi)存是否有足夠的連續(xù)空間,如果有,原地?cái)U(kuò)大可分配的內(nèi)存地址,并且返回原來的地址指針;如果空間不夠,先按照新指定的大小分配空間,將原有數(shù)據(jù)從頭到尾拷貝到新分配的內(nèi)存區(qū)域,而后釋放原來的內(nèi)存區(qū)域。?
new沒有這樣直觀的配套設(shè)施來擴(kuò)充內(nèi)存。
new和malloc內(nèi)部實(shí)現(xiàn)的區(qū)別
new
以下是從網(wǎng)上找來的一段關(guān)于new的代碼,不知道和標(biāo)準(zhǔn)的實(shí)現(xiàn)是否有區(qū)別,但是原理應(yīng)該是這樣,足夠來說明問題了:
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc) { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) if (_callnewh(size) == 0) { // report no memory _THROW_NCEE(_XSTD bad_alloc, );} return (p); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
new: 可以理解成兩步:?
1. 調(diào)用operate new()分配內(nèi)存,如果內(nèi)存不足失敗,拋出異常;?
2. 如果需要的話,在那段內(nèi)存上初始化對象(賦值或者調(diào)用構(gòu)造函數(shù)),這個應(yīng)該是由編譯器根據(jù)代碼來控制的。
因此對于new和malloc檢查是否正確分配的方法是不一樣的
int *a = (int *)malloc ( sizeof (int )); if(NULL == a) {... } else {... } 從C語言走入C++陣營的新手可能會把這個習(xí)慣帶入C++:int * a = new int(); if(NULL == a) {... } else { ... } 實(shí)際上這樣做一點(diǎn)意義也沒有,因?yàn)閚ew根本不會返回NULL,而且程序能夠執(zhí)行到if語句已經(jīng)說明內(nèi)存分配成功了,如果失敗早就拋異常了,后面的代碼就不會執(zhí)行了。正確的做法應(yīng)該是使用異常機(jī)制:try {int *a = new int(); } catch (std::bad_alloc& e) {... }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
為了照顧原來習(xí)慣的程序員,C++可以通過nothrow關(guān)鍵字來實(shí)現(xiàn)new不拋異常而是返回NULL。
int* p = new(std::nothrow) int;- 1
malloc
參考網(wǎng)上多篇文章,自己寫了一個malloc,也沒測過,如有錯誤歡迎指正。但是,用來說明malloc的原理的我想是沒問題的。
#define malloc_addr 0x00000 #define malloc_size 0x22222#ifndef NULL #define NULL 0 #endifvoid* managed_memory_start = NULL; //堆區(qū)的起始地址 void* managed_memory_end = NULL; //堆取的終止地址 int is_initialized = 0;/** 內(nèi)存控制塊,通過內(nèi)存控制塊將堆區(qū)的內(nèi)存用雙向鏈表連接起來管理*/ typedef struct {unsigned int is_available;unsigned int current_block_size;unsigned int prev_block_size; }mem_control_block;void malloc_init(void) {mem_control_block* tmp = NULL;managed_memory_start = (void*)malloc_addr;managed_memory_end = (void*)(malloc_addr + malloc_size);tmp = (mem_control_block*)managed_memory_start;tmp->is_available = 1;tmp->current_block_size = (managed_memory_end - managed_memory_start) - sizeof(mem_control_block);tmp->prev_block_size = 0;is_initialized = 1; }void* malloc(size_t size) {//初始化,最開始鏈表只有一個節(jié)點(diǎn)if(!is_initialized){malloc_init();}//保存內(nèi)存地址游標(biāo)void* current_location = NULL;//保存當(dāng)前內(nèi)存控制塊的位置mem_control_block* current_location_mcb = NULL;//如果塊太大,取下所需大小,剩余放回鏈表mem_control_block* leave_location_mcb = NULL;//定義一個用于返回的指針,返回的地址是控制塊加上前面結(jié)構(gòu)體的大小void* memory_location = NULL;//把游標(biāo)指向堆的首地址current_location = managed_memory_start;while(current_location <= managed_memory_end){current_location_mcb = (mem_control_block*)current_location;//判斷該節(jié)點(diǎn)是否被空閑if(current_location_mcb->is_available){//判斷該空閑塊大于需要,但是剩下的空間又不足以維持一個空閑塊,就把整個塊都分配了,為了維持鏈表的連續(xù)性//實(shí)際上也浪費(fèi)不了多少內(nèi)存,因?yàn)橐粋€struct結(jié)構(gòu)體很小if(current_location_mcb->current_block_size < size + 2 * sizeof(mem_control_block)){current_location_mcb->is_available = 0;break;}else //如果空閑塊大于需要的,就把剩余的塊放入leave_location_mcb節(jié)點(diǎn)了{(lán)unsigned int process_blocksize;//另當(dāng)前塊為被使用狀態(tài)current_location_mcb->is_available = 0;process_blocksize = current_location_mcb->current_block_size;current_location_mcb->current_block_size = size + sizeof(mem_control_block);leave_location_mcb = (mem_control_block*)(current_location + process_blocksize);leave_location_mcb->is_available = 1;//當(dāng)前塊大小減去需要的內(nèi)存減去一個控制塊結(jié)構(gòu)體就是剩余的大小leave_location_mcb->current_block_size = process_blocksize - sizeof(mem_control_block) - size;//leaveo塊的前一個塊的大小就是要分配的大小leave_location_mcb->prev_block_size = size + sizeof(mem_control_block);}}//如果該塊不空閑,則指針游標(biāo)指向下一塊的首地址current_location += current_location_mcb->current_block_size;}//如果空閑鏈表中已經(jīng)沒有合適的塊就擴(kuò)大堆區(qū)的范圍if(!memory_location){//申請取擴(kuò)大堆取的內(nèi)存if(sbrk(size + sizeof(mem_control_block)) != -1)return NULL;//如果空閑鏈表中沒有合適的塊,那么必然是遍歷了整個鏈表,此時的current_location_mcb指向原來空閑鏈表的最后一個塊//將該塊的大小保存下來,就是馬上要分配塊的前一個塊的大小了unsigned int prev_size = current_location_mcb->current_block_size;//之前的循環(huán)會使得current_location指向最后一個塊末尾的下一個地址current_location_mcb = (mem_control_block*)current_location;current_location_mcb->current_block_size = size + sizeof(mem_control_block);current_location_mcb->prev_block_size = prev_size;}memory_location = current_location + sizeof(mem_control_block);return memory_location; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
系統(tǒng)為堆區(qū)保存了兩個指針一個指向堆的首地址,一個指向堆的尾地址(我猜想這兩個指針是不是就是有vm_area_struct里的兩個指針維護(hù)的,源碼還沒看到只是猜測)。系統(tǒng)通過結(jié)構(gòu)體mem_control_block將整個堆區(qū)分成一個個塊,每個塊都已這樣一個結(jié)構(gòu)體開頭,結(jié)構(gòu)體里維護(hù)了塊的大小。malloc分配內(nèi)存的時候,從第一個塊開始遍歷,如果找到了塊已經(jīng)被使用,那么就找下一個塊,如果找到的塊比需要的小繼續(xù)找下一個塊,直到找到比我需要大于等于的塊,然后將該塊mem_control_block中的標(biāo)志位設(shè)置為被使用了,如果塊有剩余的就將剩余的空間添加一個mem_control_block變成一個新塊。如果遍歷了整個堆空間都沒有找到合適的塊,那么就調(diào)用sbrk函數(shù)擴(kuò)大堆的范圍。
brk() and sbrk() change the location of the?program break, which defines the end of the process’s data segment (i.e., the program break is the first location after the end of the uninitialized data segment). Increasing the program break has the effect of allocating memory to the process; decreasing the break deallocates memory.
brk() sets the end of the data segment to the value specified by addr, when that value is reasonable, the system has enough memory, and the process does not exceed its maxi‐mum data size (see setrlimit(2)).
sbrk()increments the program’s data space by increment bytes. Calling sbrk() with an increment of 0 can be used to find the current location of the program break.
上面這段摘自Linux手冊,brk函數(shù)和sbrk都是擴(kuò)大堆區(qū)(program break就是堆,因?yàn)槲闹姓f了program break就是bss后的第一個位置)尾地址的。只不過brk通過指定一個地址,而sbrk通過追加一個大小。
通過上面的代碼我們可以直到當(dāng)我們調(diào)用malloc分配n個字節(jié)的空間時,實(shí)際上占用了n + sizeof(mem_control_block)個字節(jié)的空間。但是返回的地址是緊跟結(jié)構(gòu)體mem_control_block后面的第一個地址。那么相信聰明的你已經(jīng)猜到了free是如何釋放內(nèi)存的了。沒錯,就是把指針往回倒個sizeof(mem_control_block)的距離,然后設(shè)置標(biāo)志位為可用。為了減少內(nèi)存碎片,會取查找前面一個塊和后面一個塊,如果有空閑塊就合并為一個。
給free()中的參數(shù)就可以完成釋放工作!這里要追蹤到malloc()的申請問題了。申請的時候?qū)嶋H上占用的內(nèi)存要比申請的大。因?yàn)槌龅目臻g是用來記錄對這塊內(nèi)存的管理信息。先看一下在《UNIX環(huán)境高級編程》中第七章的一段話:
大多數(shù)實(shí)現(xiàn)所分配的存儲空間比所要求的要稍大一些,額外的空間用來記錄管理信息——分配塊的長度,指向下一個分配塊的指針等等。這就意味著如果寫過一個已分配區(qū)的尾端,則會改寫后一塊的管理信息。這種類型的錯誤是災(zāi)難性的,但是因?yàn)檫@種錯誤不會很快就暴露出來,所以也就很難發(fā)現(xiàn)。將指向分配塊的指針向后移動也可能會改寫本塊的管理信息。
參考:
淺談 C++ 中的 new/delete
文章1
文章2
文章3
如何實(shí)現(xiàn)一個malloc
總結(jié)
以上是生活随笔為你收集整理的new与malloc的区别以及实现方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言面向对象编程(六):配置文件解析
- 下一篇: C语言:函数中参数的传值与传地址