浅析C\C++的动态内存管理
作者:左懒
时间:2013.5.13
声明:
原创文章,转载请标明原文链接。
个人能力有限,文章可能存在多处错误。如果您发现文中有不足或错误之处敬请批评指针。我的邮箱是: zuolanaill@gmail.com,欢迎您邮件斧正。
本文内容参考了KEIL C51和VS2012中的部分源码, 并对其进行了简单的分析和探讨,其中不乏有不确切之处,望您的批评指正。
在不同的操作系统中,C\C++的内存管理实现可能并不相同,因此本文所介绍的内容可能与您需要的内容有所出入,本文仅供参考学习。
一、KEIL IDE 中的动态内存管理
1. KEIL IDE 安装目录中的 *\Keil\C51\LIB目录下,包含了 malloc.c , calloc.c, free.c, realloc.c, init_mem.c 等源码文件,其中内容便是KEIL IDE工具提供的动态内存分配的函数。 在这里,我挑了几源文件进行了简单的分析,其中包含init_mem.c 、malloc.c 、free.c。 其它的源码文件或多或少都以这几个源码文件中的数据结构和函数进行扩充,所以也就没必要深入去研究了。
2. 首先先分析一下init_mem.c 这个文件。其中包含了一个最重要的内存池初始化函数: int init_mempool (void *pool, unsigned int size);
在分析这个函数之前,先介绍一个维护管理其动态内存的一个链表,这个链表重中之重,在C51单片机开发中,调用malloc和free进行内存的分配和释放全靠这个链表。其实这个数据结构挺简单的,只有两个数据成员。下面给出这个数据结构的定义:
1 struct __mem__ 2 { 3 struct __mem__ *next; /* 用于link堆内存中的空闲块 */ 4 unsigned int len; /* 下一个内存块的长度 */ 5 };
OK, 接下来我们看一下管理整个堆内存的头结点, 它是一个全局的变量。声明在init_mem.c 的源文件中。
1 typedef struct __mem__ __memt__; 2 typedef __memt__ *__memp__; 3 4 __memt__ __mem_avail__ [2] = 5 { 6 { NULL, 0 }, /* 用于管理堆内存的头结点,当调用完init_mempool()之后会指向首块内存块,如果堆内存已经用完,则__mem_avail__[0].next为NULL */ 7 { NULL, 0 }, /* UNUSED but necessary so free doesn't join HEAD or ROVER with the pool */ 8 /* 原代码的解释如上,目前我还搞不懂这个节点有什么用 */ 9 };
好,现在介绍一下重点的东西。现在看一下内存池的初始化函数,其实它的工作很简单。不过记住,内存池初始化函数只能够被调用一次。多次调用的话上几次的内存将无法被释放。
1 /* 初始化内存池, */ 2 int init_mempool (void *pool, unsigned int size) 3 { 4 5 if (size < MIN_POOL_SIZE) /* 如果内存小于定义最小的内存池,则返回内存池初始化失败 */ 6 return (0); 7 8 9 if (pool == NULL) 10 { 11 pool = (void *)1; /* 如果内存池传入的是NULL,则将内存池指向地址为1的位置。 * 12 /* 因为C51单片机的数据段是存储在XDATA, 大小为0x0 ~ 0xffff 共64K可用 */ 13 size--; /* 取size-1的内存大小作为堆内存 */ 14 /* 注意的是,若是在X86兼容机不能这么写,因为X86的机子是有内存保护的 */ 15 } 16 17 18 AVAIL.next = (__memp__)pool; /* 指向堆内存 */ 19 AVAIL.len = size; /* 堆内存大小 */ 20 21 22 (AVAIL.next)->next = NULL; /* 当前的堆内存还是一块空闲的内存 */ 23 (AVAIL.next)->len = size - HLEN; /* 除去管理堆内存的头结点信息,还剩下多少空间可用 */ 24 25 return (-1); /* 返回成功 */ 26 }
看完这个其实内存初始化的工作还是挺简单的。下面我给出调用 init_mempool()后头结点大概的示意图(画得有点戳,请见谅):
看过图应该还是挺直观的。
如果您你自己查看KEIL 安装目录里的 init_mem.c 源码会发现跟我贴出来的内容有所区别, 它可能如下:
1 /*----------------------------------------------------------------------------- 2 INIT_MEM.C is part of the C51 Compiler package from Keil Software. 3 Copyright (c) 1995-2002 Keil Software. All rights reserved. 4 -----------------------------------------------------------------------------*/ 5 #include <stdlib.h> 6 7 /*----------------------------------------------- 8 Memory pool block structure and typedefs. 9 Memory is laid out as follows: 10 11 {[NXT|LEN][BLK (LEN bytes)]}{[NXT|LEN][BLK]}... 12 13 Note that the size of a node is: 14 __mem__.len + sizeof (__mem__) 15 -----------------------------------------------*/ 16 struct __mem__ 17 { 18 struct __mem__ _MALLOC_MEM_ *next; /* single-linked list */ 19 unsigned int len; /* length of following block */ 20 }; 21 22 typedef struct __mem__ __memt__; 23 typedef __memt__ _MALLOC_MEM_ *__memp__; 24 25 #define HLEN (sizeof(__memt__)) 26 27 /*----------------------------------------------- 28 Memory pool headers. AVAIL points to the first 29 available block or is NULL if there are no free 30 blocks. ROVER is a roving header that points to 31 a block somewhere in the list. 32 33 Note that the list is maintained in address 34 order. AVAIL points to the block with the 35 lowest address. That block points to the block 36 with the next higher address and so on. 37 -----------------------------------------------*/ 38 __memt__ _MALLOC_MEM_ __mem_avail__ [2] = 39 { 40 { NULL, 0 }, /* HEAD for the available block list */ 41 { NULL, 0 }, /* UNUSED but necessary so free doesn't join HEAD or ROVER with the pool */ 42 }; 43 44 #define AVAIL (__mem_avail__[0]) 45 46 #define MIN_POOL_SIZE (HLEN * 10) 47 48 /*----------------------------------------------------------------------------- 49 int init_mempool ( 50 void _MALLOC_MEM_ *pool, address of the memory pool 51 unsigned int size); size of the pool in bytes 52 53 Return Value 54 ------------ 55 0 FAILURE: Memory pool is not large enough 56 NZ SUCCESS: Memory pool management initialized. 57 -----------------------------------------------------------------------------*/ 58 int init_mempool ( 59 void _MALLOC_MEM_ *pool, 60 unsigned int size) 61 { 62 /*----------------------------------------------- 63 Verify that the pool is large enough to actually 64 do something. If it is too small, exit with 0. 65 -----------------------------------------------*/ 66 if (size < MIN_POOL_SIZE) 67 return (0); /* FAILURE */ 68 69 /*----------------------------------------------- 70 If the pool points to the beginning of a memory 71 area (NULL), change it to point to 1 and decrease 72 the pool size by 1 byte. 73 -----------------------------------------------*/ 74 if (pool == NULL) 75 { 76 pool = 1; 77 size--; 78 } 79 80 /*----------------------------------------------- 81 Set the AVAIL header to point to the beginning 82 of the pool and set the pool size. 83 -----------------------------------------------*/ 84 AVAIL.next = pool; 85 AVAIL.len = size; 86 87 /*----------------------------------------------- 88 Set the link of the block in the pool to NULL 89 (since it's the only block) and initialize the 90 size of its data area. 91 -----------------------------------------------*/ 92 (AVAIL.next)->next = NULL; 93 (AVAIL.next)->len = size - HLEN; 94 95 return (-1); /* SUCCESS */ 96 }
KEIL 工具所带的源文件跟我贴出来的代码有所不同。其原因如下:
第一:其中在数据结构的声明和头结点的定义都多出了一个 _MALLOC_MEM_ , 但是这个_MALLOC_MEM_标识符在我的KEIL中是没办法跟踪出来的,据我个人理解这个_MALLOC_MEM_ 可能是用#define _MALLOC_MEM_ 定义的一个空的宏定义,用于标识这个数据结构用于动态分配所用。所以在我贴出来的代码中去除了这个_MALLOC_MEM_标识符。尽量让代码看起来简洁舒服。
第二: 里面一些不同类型的指针直接赋值也被我用上了强制转换后再赋值,避免某些编译器会报错。
3. malloc.c 源文件分析
在malloc.c源文件中malloc函数原型是:void * malloc(unsigned int size); size为需要申请动态内存的字节大小。但是实际上它在分配内存的时候不仅仅只分配size个字节内存,它还会多为它分配一个管理这个分配内存块的管理节点。所以它分配的内存字节数应该是: size+sizeof(__mem__); 其中__mem__便是init_mem.c源文件中的链表节点。
malloc的算法大概是:查找AVAIL链表中next域下各个节点空闲的内存块,如果发现这个空闲块>=size的大小,则停止查找并进行内存分配。如果找不到合适的内存块,则返回NULL。
如果所找到的空闲块分配size个字节之后所剩的内存并不多,则将空闲块分配给应用程序后,然后将这一块从AVAIL链表中直接除去。如果空闲块所剩较多,则分割这个空闲块,将分割出来的空闲块链在AVAIL链表中。
malloc 的源码如下:
1 void * _malloc ( unsigned int size) 2 { 3 __memp__ q; /* 指向的是内存空闲块 */ 4 __memp__ p; /* q->next */ 5 unsigned int k; /* 剩下的空闲内存块大小 */ 6 7 8 q = &AVAIL; 9 10 11 /* 在AVATL链表中查找空闲的堆内存 */ 12 while (1) 13 { 14 if ((p = q->next) == NULL) 15 { 16 return (NULL); /* 没有空闲的堆内存,返回NULL */ 17 } 18 19 if (p->len >= size) /* 找到一个空闲的内存块,并且这个内存块大于所要分配的大小 */ 20 break; 21 22 q = p; 23 } 24 25 k = p->len - size; /* 申请内存后还剩下多少内存 */ 26 27 if (k < MIN_BLOCK) /* 如果分配的内存太大,超过了最小堆内存块的定义,则一次性把堆内存给分配完,并把AVAIL结点的next域置NULL,表示没有堆内存可再次分配了 */ 28 { 29 q->next = p->next; 30 return (&p[1]); /* 返回分配的内存指针 */ 31 } 32 33 34 k -= HLEN; /* 多分配一个管理分配内存的节点,所以k要多减去一个节点的字节数 */ 35 p->len = k; /* p所指的是空闲块 */ 36 37 38 q = (__memp__) (((char *) (&p [1])) + k); /* q指向分配内存的起始地址。注意,这里的q所指向的地址是有加上管理分配内存节点的。 */ 39 q->len = size; /* 分配内存的大小,也就是malloc 参数中size的大小,它不包含管理节点的大小 */ 40 41 return (&q[1]); /* 返回分配的内存的指针 */ 42 }
当你调用 int *p = (int *)malloc(40); 之后它的内存分布情况可能如下:
malloc.c源码分析就此告一段落,因为malloc.c源文件中主要也是这个malloc函数。当然,下面我会给出KEIL 安装目录下malloc.c的源码供大家参考对比:
1 /*----------------------------------------------------------------------------- 2 MALLOC.C is part of the C51 Compiler package from Keil Software. 3 Copyright (c) 1995-2002 Keil Software. All rights reserved. 4 -----------------------------------------------------------------------------*/ 5 #include <stdlib.h> 6 7 /*----------------------------------------------- 8 Memory pool block structure and typedefs. 9 Memory is laid out as follows: 10 11 {[NXT|LEN][BLK (LEN bytes)]}{[NXT|LEN][BLK]}... 12 13 Note that the size of a node is 14 __mem__.len + sizeof (__mem__) 15 -----------------------------------------------*/ 16 struct __mem__ 17 { 18 struct __mem__ _MALLOC_MEM_ *next; /* single-linked list */ 19 unsigned int len; /* length of following block */ 20 }; 21 22 typedef struct __mem__ __memt__; 23 typedef __memt__ _MALLOC_MEM_ *__memp__; 24 25 #define HLEN (sizeof(__memt__)) 26 27 /*----------------------------------------------- 28 Memory pool headers. AVAIL points to the first 29 available block or is NULL if there are no free 30 blocks. 31 32 Note that the list is maintained in address 33 order. AVAIL points to the block with the 34 lowest address. That block points to the block 35 with the next higher address and so on. 36 -----------------------------------------------*/ 37 extern __memt__ _MALLOC_MEM_ __mem_avail__ []; 38 39 #define AVAIL (__mem_avail__[0]) 40 41 #define MIN_BLOCK (HLEN * 4) 42 43 /*----------------------------------------------------------------------------- 44 void _MALLOC_MEM_ *malloc ( 45 unsigned int size); number of bytes to allocate 46 47 Return Value 48 ------------ 49 NULL FAILURE: No free blocks of size are available 50 NON-NULL SUCCESS: Address of block returned 51 -----------------------------------------------------------------------------*/ 52 void _MALLOC_MEM_ *malloc ( 53 unsigned int size) 54 { 55 __memp__ q; /* ptr to free block */ 56 __memp__ p; /* q->next */ 57 unsigned int k; /* space remaining in the allocated block */ 58 59 /*----------------------------------------------- 60 Initialization: Q is the pointer to the next 61 available block. 62 -----------------------------------------------*/ 63 q = &AVAIL; 64 65 /*----------------------------------------------- 66 End-Of-List: P points to the next block. If 67 that block DNE (P==NULL), we are at the end of 68 the list. 69 -----------------------------------------------*/ 70 while (1) 71 { 72 if ((p = q->next) == NULL) 73 { 74 return (NULL); /* FAILURE */ 75 } 76 77 /*----------------------------------------------- 78 Found Space: If block is large enough, reserve 79 if. Otherwise, copy P to Q and try the next 80 free block. 81 -----------------------------------------------*/ 82 if (p->len >= size) 83 break; 84 85 q = p; 86 } 87 88 /*----------------------------------------------- 89 Reserve P: Use at least part of the P block to 90 satisfy the allocation request. At this time, 91 the following pointers are setup: 92 93 P points to the block from which we allocate 94 Q->next points to P 95 -----------------------------------------------*/ 96 k = p->len - size; /* calc. remaining bytes in block */ 97 98 if (k < MIN_BLOCK) /* rem. bytes too small for new block */ 99 { 100 q->next = p->next; 101 return (&p[1]); /* SUCCESS */ 102 } 103 104 /*----------------------------------------------- 105 Split P Block: If P is larger than we need, we 106 split P into two blocks: the leftover space and 107 the allocated space. That means, we need to 108 create a header in the allocated space. 109 -----------------------------------------------*/ 110 k -= HLEN; 111 p->len = k; 112 113 q = (__memp__ ) (((char _MALLOC_MEM_ *) (&p [1])) + k); 114 q->len = size; 115 116 return (&q[1]); /* SUCCESS */ 117 }
实际上里面核心的东西就malloc这个函数而已。
4. free.c源文件的分析
free函数在free.c源文件中的声明是: void _free ( void *memp); 其中memp参数是要进行内存释放的指针。
free函数的基本算法是:在AVAIL链表中查找一个合适的节点,然后将它挂到链表中。在将memp挂到链表之前,会检查memp相邻之间是否有空闲的内存块,如果有,则将两则合并后再挂到链表中,如果没有,则直接挂到链表中。
free函数源码如下:
void _free ( void *memp) { __memp__ q; /* 指向空闲块 */ __memp__ p; /* q->next */ __memp__ p0; /* 要被释放的指针 */ if ((memp == NULL) || (AVAIL.len == 0)) return; p0 = (__memp__)memp; p0 = &p0 [-1]; q = &AVAIL; /* 查找一个可以挂载memp的合适的挂载节点 */ /* 经过这个算法查找,空闲内存块的开始地址会按升序排序 */ while (1) { p = q->next; if ((p == NULL) || (p > memp)) break; q = p; } /* memp之后是否有空闲内存,若有则合并内存块 */ if ((p != NULL) && ((((char *)memp) + p0->len) == (char *)p)) { p0->len += p->len + HLEN; p0->next = p->next; } else { p0->next = p; } /* memp之前是否有空闲内存,若有则合并后挂到链表中,若没有则直接挂到链表 */ if ( ( ((char *)q) + q->len + HLEN ) == (char *)p0 ) { q->len += p0->len + HLEN; q->next = p0->next; } else { q->next = p0; } }
int *p = (int *)_malloc (sizeof(int)*10);
int *q = (int *)_malloc (sizeof(int));
_free (p);
当调用以上代码之后,它的内存分布情况可能如下:
从代码之中也可以发现,内存释放之后,管理p内存块的内容并不会做任何处理。所以下一次分配内存的时候再被分配到这一块内存的时候,内存的内容还可能残留着上一次分配后使用过的内容。
下面给出free.c中的源码内容
1 /*----------------------------------------------------------------------------- 2 FREE.C is part of the C51 Compiler package from Keil Software. 3 Copyright (c) 1995-2002 Keil Software. All rights reserved. 4 -----------------------------------------------------------------------------*/ 5 #include "stdlib.h" 6 7 /*----------------------------------------------- 8 Memory pool block structure and typedefs. 9 Memory is laid out as follows: 10 11 {[NXT|LEN][BLK (LEN bytes)]}{[NXT|LEN][BLK]}... 12 13 Note that the size of a node is: 14 __mem__.len + sizeof (__mem__) 15 -----------------------------------------------*/ 16 struct __mem__ 17 { 18 struct __mem__ _MALLOC_MEM_ *next; /* single-linked list */ 19 unsigned int len; /* length of following block */ 20 }; 21 22 typedef struct __mem__ __memt__; 23 typedef __memt__ _MALLOC_MEM_ *__memp__; 24 25 #define HLEN (sizeof(__memt__)) 26 27 /*----------------------------------------------- 28 Memory pool headers. AVAIL points to the first 29 available block or is NULL if there are no free 30 blocks. ROVER is a roving header that points to 31 a block somewhere in the list. 32 33 Note that the list is maintained in address 34 order. AVAIL points to the block with the 35 lowest address. That block points to the block 36 with the next higher address and so on. 37 -----------------------------------------------*/ 38 extern __memt__ _MALLOC_MEM_ __mem_avail__ []; 39 40 #define AVAIL (__mem_avail__[0]) 41 42 /*----------------------------------------------------------------------------- 43 -----------------------------------------------------------------------------*/ 44 void free ( 45 void _MALLOC_MEM_ *memp) 46 { 47 /*----------------------------------------------- 48 FREE attempts to organize Q, P0, and P so that 49 Q < P0 < P. Then, P0 is inserted into the free 50 list so that the list is maintained in address 51 order. 52 53 FREE also attempts to consolidate small blocks 54 into the largest block possible. So, after 55 allocating all memory and freeing all memory, 56 you will have a single block that is the size 57 of the memory pool. The overhead for the merge 58 is very minimal. 59 -----------------------------------------------*/ 60 __memp__ q; /* ptr to free block */ 61 __memp__ p; /* q->next */ 62 __memp__ p0; /* block to free */ 63 64 /*----------------------------------------------- 65 If the user tried to free NULL, get out now. 66 Otherwise, get the address of the header of the 67 memp block (P0). Then, try to locate Q and P 68 such that Q < P0 < P. 69 -----------------------------------------------*/ 70 if ((memp == NULL) || (AVAIL.len == 0)) 71 return; 72 73 p0 = memp; 74 p0 = &p0 [-1]; /* get address of header */ 75 76 /*----------------------------------------------- 77 Initialize. 78 Q = Location of first available block. 79 -----------------------------------------------*/ 80 q = &AVAIL; 81 82 /*----------------------------------------------- 83 B2. Advance P. 84 Hop through the list until we find a free block 85 that is located in memory AFTER the block we're 86 trying to free. 87 -----------------------------------------------*/ 88 while (1) 89 { 90 p = q->next; 91 92 if ((p == NULL) || (p > memp)) 93 break; 94 95 q = p; 96 } 97 98 /*----------------------------------------------- 99 B3. Check upper bound. 100 If P0 and P are contiguous, merge block P into 101 block P0. 102 -----------------------------------------------*/ 103 if ((p != NULL) && ((((char _MALLOC_MEM_ *)memp) + p0->len) == p)) 104 { 105 p0->len += p->len + HLEN; 106 p0->next = p->next; 107 } 108 else 109 { 110 p0->next = p; 111 } 112 113 /*----------------------------------------------- 114 B4. Check lower bound. 115 If Q and P0 are contiguous, merge P0 into Q. 116 -----------------------------------------------*/ 117 if ((((char _MALLOC_MEM_ *)q) + q->len + HLEN) == p0) 118 { 119 q->len += p0->len + HLEN; 120 q->next = p0->next; 121 } 122 else 123 { 124 q->next = p0; 125 } 126 } 127 \
好,keil 中附带的源码也差不多分析到这里了。下面给出我移植到VS2008的内存管理源码:
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 6 static char heap_mem[1024]; /* 模拟堆内存 */ 7 8 #define HLEN (sizeof(__memt__)) 9 #define AVAIL (__mem_avail__[0]) /* 用于代替__mem_avail__[0]的宏定义 */ 10 #define MIN_POOL_SIZE (HLEN * 10) 11 #define MIN_BLOCK (HLEN * 4) 12 13 14 struct __mem__ 15 { 16 struct __mem__ *next; /* 用于连接堆内存中的空闲块 */ 17 unsigned int len; /* 下一个内存块的长度 */ 18 }; 19 20 typedef struct __mem__ __memt__; 21 typedef __memt__ *__memp__; 22 23 __memt__ __mem_avail__ [2] = 24 { 25 { NULL, 0 }, /* 用于管理堆内存的头结点,当调用完init_mempool()之后会指向首块内存块,如果堆内存已经用完,则__mem_avail__[0].next为NULL */ 26 { NULL, 0 }, /* UNUSED but necessary so free doesn't join HEAD or ROVER with the pool */ 27 /* 原文的解释如上 */ 28 }; 29 30 31 32 /* 初始化内存池, */ 33 int init_mempool (void *pool, unsigned int size) 34 { 35 36 if (size < MIN_POOL_SIZE) /* 如果内存小于定义最小的内存池,则返回内存池初始化失败 */ 37 return (0); 38 39 40 if (pool == NULL) 41 { 42 pool = (void *)1; /* 如果内存池传入的是NULL,则将内存池指向地址为1的位置。 * 43 /* 因为C51单片机的数据段是存储在XDATA, 大小为0x0 ~ 0xffff 共64K可用 */ 44 size--; /* 取size-1的内存大小作为堆内存 */ 45 /* 注意的是,若是在X86兼容机不能这么写,因为X86的机子是有内存保护的 */ 46 } 47 48 49 AVAIL.next = (__memp__)pool; /* 指向堆内存 */ 50 AVAIL.len = size; /* 堆内存大小 */ 51 52 53 (AVAIL.next)->next = NULL; /* 当前的堆内存还是一块空闲的内存 */ 54 (AVAIL.next)->len = size - HLEN; /* 除去管理堆内存的头结点信息,还剩下多少空间可用 */ 55 56 return (-1); /* 返回成功 */ 57 } 58 59 void * _malloc ( unsigned int size) 60 { 61 __memp__ q; /* 指向的是内存空闲块 */ 62 __memp__ p; /* q->next */ 63 unsigned int k; /* 剩下的空闲内存块大小 */ 64 65 66 q = &AVAIL; 67 68 69 /* 在AVATL链表中查找空闲的堆内存 */ 70 while (1) 71 { 72 if ((p = q->next) == NULL) 73 { 74 return (NULL); /* 没有空闲的堆内存,返回NULL */ 75 } 76 77 if (p->len >= size) /* 找到一个空闲的内存块,并且这个内存块大于所要分配的大小 */ 78 break; 79 80 q = p; 81 } 82 83 k = p->len - size; /* 申请内存后还剩下多少内存 */ 84 85 if (k < MIN_BLOCK) /* 如果分配的内存太大,超过了最小堆内存块的定义,则一次性把堆内存给分配完,并把AVAIL结点的next域置NULL,表示没有堆内存可再次分配了 */ 86 { 87 q->next = p->next; 88 return (&p[1]); /* 返回分配的内存指针 */ 89 } 90 91 92 k -= HLEN; /* 多分配一个管理分配内存的节点,所以k要多减去一个节点的字节数 */ 93 p->len = k; /* p所指的是空闲块 */ 94 95 96 q = (__memp__) (((char *) (&p [1])) + k); /* q指向分配内存的起始地址。注意,这里的q所指向的地址是有加上管理分配内存节点的。 */ 97 q->len = size; /* 分配内存的大小,也就是malloc 参数中size的大小,它不包含管理节点的大小 */ 98 99 return (&q[1]); /* 返回分配的内存的指针 */ 100 } 101 102 void _free ( void *memp) 103 { 104 105 __memp__ q; /* 指向空闲块 */ 106 __memp__ p; /* q->next */ 107 __memp__ p0; /* 要被释放的指针 */ 108 109 if ((memp == NULL) || (AVAIL.len == 0)) 110 return; 111 112 p0 = (__memp__)memp; 113 p0 = &p0 [-1]; 114 115 116 q = &AVAIL; 117 118 119 120 /* 查找一个可以挂载memp的合适的挂载节点 */ 121 /* 经过这个算法查找,空闲内存块的开始地址会按升序排序 */ 122 while (1) 123 { 124 p = q->next; 125 126 if ((p == NULL) || (p > memp)) 127 break; 128 129 q = p; 130 } 131 132 133 /* memp之后是否有空闲内存,若有则合并内存块 */ 134 if ((p != NULL) && ((((char *)memp) + p0->len) == (char *)p)) 135 { 136 p0->len += p->len + HLEN; 137 p0->next = p->next; 138 } 139 else 140 { 141 p0->next = p; 142 } 143 144 /* memp之前是否有空闲内存,若有则合并后挂到链表中,若没有则直接挂到链表 */ 145 /* 加上 q != &AVAIL是为了不让当q == AVAIL时丢失pool内存池 */ 146 if ( ( q != &AVAIL ) && ( ((char *)q) + q->len + HLEN ) == (char *)p0 ) 147 { 148 q->len += p0->len + HLEN; 149 q->next = p0->next; 150 } 151 else 152 { 153 q->next = p0; 154 } 155 } 156 157 158 int main (void) 159 { 160 //只可以调用一次。 161 init_mempool(heap_mem, sizeof(heap_mem)); 162 163 int *p = (int *)_malloc (sizeof(int)*10); 164 int *q = (int *)_malloc (sizeof(int)); 165 166 167 _free (p); 168 169 170 171 172 173 return 0; 174 }
5.回顾总结一下C\C++中内存管理:
a) 在调用 void* malloc(unsigned int size) 函数进行内存分配的时候,系统(嵌入式的机子裸机跑可能没有上系统) 或者说是C运行库或分配的内容绝不仅仅是size个大小,它还要加上一个管理这个分配的内存块的节点信息,一般放在这个内存块之前(Windows中也是放在分配的内存块之前)。
b) 调用void free(void *memp) 进行内存释放的时候,其实就是将memp所指内存块挂到管理内存块的链表上去。
c) 通过例子也可以发现,当多次malloc 和free之后内存碎片的情况将会越来越严重。这也是动态分配内存的一个弱点吧。
d) 计算机中并没有将内存分为堆或栈,对内存进行分类的仅仅是我们计算机的使用者。