首先将new与malloc的区别总结如下:
1 new可以自动计算需要分配多大的内存,而malloc必须指明。
2 new返回的指针是有类型的,malloc返回void*类型的指针。
3 new在分配内存的同时会调用构造函数。
4 new是c++运算符,malloc是标准库函数。
由于3所述,malloc在c++中无法满足为非内部对象分配内存的需求。
那么malloc是如何进行内存分配的呢?它将内存块连接为一个链表。调用malloc函数时,它沿链表寻找一个大到足以满足用户请求所需要的空闲内存块。然后,将该内存块传给用户(如果该内存块比用户申请的内存大很多,可进行分割),并将该内存块标记为已用。(free的过程待补充)
malloc具体实现参考:
http://blog.codinglabs.org/articles/a-malloc-tutorial.html
该文对Linux系统内存相关知识的介绍也十分清楚。现将其malloc实现简单总结如下:
内存按块划分,每个块由meta区和数据区组成,meta区记录数据区的大小、是否空闲并指向下一个内存块。数据区是真实分配的内存,malloc返回给用户的就是数据区的首地址。首先定义表示内存块的数据结构:
typedef struct s_block* t_block; struct s_block { size_t size; int free; t_block next; int padding; char data[1]; };
然后实现查找内存块的find_block,传入需要分配的内存大小,返回找到的指向内存块的指针:
t_block find_block(t_block* last, size_t s) { t_block b = first_block; while (b&&!(b->free && b->size >= s)) { *last = b; b = b->next; } return b; }
从头开始搜索链表,对于不符合条件的节点,last立即跟踪,遇到符合条件的节点或空节点循环结束。last是为了当遍历完链表没有符合条件的节点,即b=NULL时,记录最后一个节点,方便后面做扩展。做扩展的代码如下:
#define BLOCKSIZE 24 t_block extend_block(t_block last, size_t s) { t_block b = sbrk(0); if (sbrk(s + BLOCKSIZE) == (void*)-1) return NULL; b->size = s; b->free = 0; b->next = last->next; last->next = b; return b; }
即用系统调用sbrk()在堆上新找到个节点,并将其接到原链表。malloc还要做的一个工作就是如果内存块比较大,将其分给一个申请了很小内存空间的用户会显得很浪费,此时malloc应将该内存块分割,将剩余的内存作为新节点放在链表上:
void split_block(t_block b, size_t s) { t_block newb; newb = (t_block)b->data + s; newb->size = b->size - s - BLOCKSIZE; newb->free = 1; newb->next = b->next; b->next = newb; b->size = s; }
有了以上的函数,malloc就可以简单的实现如下:
void* first_block=NULL; void* malloc(size_t size) { size_t s; s = align(size); t_block b=first_block; t_block last; if (b) { b = find_block(&last, s); if (b) { if (b->size >= (s + BLOCKSIZE + 8)) split_block(b, s); b->free = 0; } else { b = extend_block(last, s); } } else { b = extend_block(NULL, s); first_block = b; } return b; }