【C】简单的malloc和free的实现
这里简单实现了cstdlib的动态内存管理函数malloc、calloc、realloc和free大致原理是先申请一个数组作为内存空间,然后使用链表连接每个申请的内存块。如果要释放内存,就从链表中移除相应的内存块。
在没有动态内存管理的环境中,以下代码能派上用场替代malloc函数等。
使用前需要先调用mpkInitMemory函数。
这是头文件:
#ifndef _MEMPKG_H_
#define _MEMPKG_H_
#include <stddef.h> /* Using type size_t. */
typedef unsigned char UCHART, * PUCHAR;
typedef struct st_BLOCK_HEADER
{
struct st_BLOCK_HEADER * pnext;
PUCHAR pblock;
size_t size;
} BLOCK_HEADER, * P_BLOCK_HEADER;
#define MEM_SIZ (8192) /* Alter this macro to control the size of a heap. */
void mpkInitMemory(void);
void * memcpy (void * dst, void * src, size_t size);
void * memset (void * s, int c, size_t n);
void * malloc (size_t size);
void free (void * ptr);
void * realloc (void * ptr, size_t size);
void * calloc (size_t n, size_t size);
int memcmp (const void * s1,const void * s2,size_t n);
#define memmove(dst, src, size) memcpy(dst, src, size)
#endif
源文件:
#include "mempkg.h"
UCHART volatile array; /* Create a linear address space. */
const PUCHAR volatile pmem = array;
volatile P_BLOCK_HEADER phead = NULL; /* This is the header pointer of block chain. */
/* Invoke this function before you use any function of this package.
*/
void mpkInitMemory(void)
{
phead = (P_BLOCK_HEADER)pmem;
phead->pblock = pmem + sizeof(BLOCK_HEADER);
phead->pnext = (P_BLOCK_HEADER)(pmem + MEM_SIZ - sizeof(BLOCK_HEADER));
phead->size = 0;
phead->pnext->pblock = pmem + MEM_SIZ;
phead->pnext->pnext = NULL;
phead->pnext->size = 0;
}
void * memcpy(void * dst, void * src, size_t size)
{
char * sc1;
const char * sc2;
sc1 = dst;
sc2 = src;
if (sc2 < sc1 && sc1 < sc2 + size)
for (sc1 += size, sc2 += size; 0 < size; --size)
*--sc1 = *--sc2;
else
for (; 0 < size; --size)
*sc1++ = *sc2++;
return dst;
}
void * memset(void * s, int c, size_t n)
{
const UCHART uc = c;
unsigned char * su;
for (su = s; 0 < n; ++su, --n)
*su = uc;
return s;
}
void * malloc(size_t size)
{
if (size > 0)
{
P_BLOCK_HEADER pnode, pnew;
for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
{
if (pnode->pnext != NULL)
{ /* Test available space for the new block. */
if (pnode->pnext->pblock - sizeof(BLOCK_HEADER) - pnode->pblock - pnode->size >=
size + sizeof(BLOCK_HEADER))
{
pnew = (P_BLOCK_HEADER)(pnode->pblock + pnode->size);
/* Initialize the new block header and assign new block. */
pnew->pblock = pnode->pblock + pnode->size + sizeof(BLOCK_HEADER);
pnew->size = size;
pnew->pnext = pnode->pnext;
pnode->pnext = pnew;
return pnew->pblock;
}
}
else
break;
}
}
return NULL;
}
void free(void * ptr)
{ /* Preserve the tail and header block structure. */
if (pmem == ptr || pmem + MEM_SIZ == ptr)
return;
if (NULL != ptr)
{
P_BLOCK_HEADER pnode;
for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
{
if (NULL != pnode->pnext && ptr == pnode->pnext->pblock)
{
pnode->pnext = pnode->pnext->pnext;
return;
}
}
}
return;
}
void * realloc(void * ptr, size_t size)
{ /* Preserve the tail and header block structure. */
if (pmem == ptr || pmem + MEM_SIZ == ptr)
return NULL;
if (NULL == ptr && size > 0)
return malloc(size);
else if (size > 0)
{
P_BLOCK_HEADER pnode;
for (pnode = phead; NULL != pnode; pnode = pnode->pnext)
{
if (NULL != pnode->pnext && ptr == pnode->pnext->pblock)
{
if (size == pnode->pnext->size)
return ptr;
else if (pnode->pnext->pnext->pblock - sizeof(BLOCK_HEADER) - pnode->pblock - pnode->size >=
size + sizeof(BLOCK_HEADER))
{
pnode->pnext->size = size;
return ptr;
}
else
{
void * newptr = malloc(size);
if (NULL != newptr)
{
memcpy(newptr, ptr, pnode->pnext->size);
free(ptr);
}
return newptr;
}
}
}
}
return NULL;
}
void * calloc(size_t n, size_t size)
{
void * rtn = malloc(size * n);
memset(rtn, 0, size * n);
return rtn;
}
int memcmp(const void * s1, const void * s2, size_t n)
{
PUCHAR su1, su2;
for (su1 = s1, su2 = s2; 0 < n; ++su1, ++su2, --n)
if (*su1 != *su2)
return *su1 < *su2 ? -1 : 1;
return 0;
}
一种裸机上管理内存的感觉,让我想到了在DOS年代,捅穿A20地址线,然后写16 MB内存的一些DOS游戏。
但,像STM32这样的单片机上,然后像STM32CubeIDE这样的开发环境提供的malloc其实是静态分配地址,就像一个宏,它给你局部起了一个static char mem;然后这块内存妥妥地进入了链接器脚本的.bss段。对应的,单片机上的free是没有任何行为的。
另外,现在的桌面机或者服务器环境,我为了实现高频的内存分配释放的效果,写过内存池,然后感觉一个比较大的难点是如何实现多线程环境下,能尽可能不锁住线程,实现高效的内存分配释放。 这段代码还有个缺点就是容易形成内存碎片,本来想实现一个伙伴系统的无奈功夫不到家没有实现成功。
在多线程环境下这段代码必须加锁,不然内存会被写乱。
另外,楼上可以参考一下glibc的malloc来获得灵感。
页:
[1]