CSAPP-Lab6:MallocLab

该实验要求编写一个C语言动态存储分配器,包括mm_init,mm_malloc,mm_free,mm_realloc等函数,涉及内存初始化、空闲块管理、内存扩展、最佳适应和首次适应算法以及内存碎片的合并。实验旨在创建一个正确、高效和快速的内存分配器。
摘要由CSDN通过智能技术生成

目录

1、实验文档注释

2、实验题目

2.1 基本常数、宏、函数

2.2 函数

mm_init

extend_heap

coalesce

 mm_malloc

 first_fit

 best_fit

 place

 mm_free

 mm_realloc

3、总结


1、实验文档注释

在本实验中,你将为C语言程序编写一个动态存储分配器,即你自己版本的 malloc、free 和 realloc程序。我们鼓励你创造性地探索设计空间,并且 实现一个正确、高效和快速的分配器。

你的动态存储分配器将由以下四个函数组成,它们在 mm.h 中声明 中声明,并在 mm.c 中定义

int mm_init(void);
void *mm_malloc(size_t size);
void mm_free(void *ptr);
void *mm_realloc(void *ptr, size_t size);

2、实验题目

2.1 基本常数、宏、函数

/* 基本常数和宏 */
#define WSIZE 4    /* 字 */
#define DSIZE 8    /* 双字 */
#define CHUNKSIZE (1 << 12)    /* 初始空闲块和扩展堆的默认大小 */

#define MAX(x, y) ((x) > (y) ? (x) : (y))

/* 将块大小和已分配位结合为一个值,可以存在头部和脚部 */
/* 此处注意 size 的低 3 位一定是空的,因为大小是 8 的倍数 */
/* alloc 占第 0 位 */
#define PACK(size, alloc) ((size) | (alloc))

/* 读写指针 p 的位置 */
/* 读取和返回参数 p 引用的字 */
#define GET(p) (*(unsigned int *)(p))   
/* 必须类型转换,因为 p 是 void* */ 
#define PUT(p, val) (*(unsigned int *)(p) = (val))    

/* 头部或脚部调用,获取大小 */
#define GET_SIZE(p) (GET(p) & ~0x07)
/* 头部或脚部调用,获取分配位 */
#define GET_ALLOC(p) (GET(p) & 0x01)

/* 给定有效载荷,找到头部指针 */
#define HDRP(bp) ((char *)(bp) - WSIZE)
/* 给定有效载荷,找到脚部指针 */
#define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)

/* 找到下一个块 */
#define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
/* 找到前一个块 */
#define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))

static void *extend_heap(size_t words);
static void place(void *bp, size_t asize);
static void *first_fit(size_t asize);
static void *best_fit(size_t asize);
static void *coalesce(void *bp);

2.2 函数

在调用 mm_malloc、mm_realloc 或 mm_free 之前,应用程序(即、 你将用来评估你的实现的跟踪驱动程序)调用mm_init来执行任何必要的初始化。执行任何必要的初始化,如分配初始堆区。返回值应该是 如果在执行初始化时有问题,返回值应为-1,否则为0。

mm_init函数从内存系统得到 4 个字,并将其初始化,创建一个空的空闲链表。然后调用 extend_heap 函数,用来扩展 CHUNKSIZE 字节,并创建初始的空闲块。

mm_init

int mm_init(void){
	//分配4 * 4大小
	if((heap_listp = mem_sbrk(4 * WSIZE)) == (void *)-1){
		return -1;
	}
	
	// 序言块和结尾块是消除边界合并时边界条件的技巧
	PUT(heap_listp, 0);	// 双字对齐
	PUT(heap_listp + (1 * WSIZE), PACK(DSIZE, 1));	// 特殊序言块头
	PUT(heap_listp + (2 * WSIZE), PACK(DSIZE, 1));	// 特殊序言块尾
	PUT(heap_listp + (3 * WSIZE), PACK(0, 1));	// 结尾块,大小为0,整个堆就一个
	// 有效载荷位置
	heap_listp += (2 * WSIZE);
	
	// 扩展空闲空间
	if(extend_heap(CHUNKSIZE / WSIZE) == NULL){
		return -1;
	}
    return 0;
}

extend_heap

static void *extend_heap(size_t words){
	char *bp;
	size_t size;
	
	// 确定拓展字节数并对齐
	size = (words % 2) ? (words + 1) * WSIZE : words * WSIZE;
	
	// bp指向新分配内存起始位置,因为会覆盖前面,所以也就是有效载荷的位置
	if((long)(bp = mem_sbrk(size)) == -1){
		return NULL;
	}
	
	// 这里HDRP可以看出,其实头部序言块是占有了上一个的结尾块,依次前移了
	PUT(HDRP(bp), PACK(size, 0));	// 设置头部
	PUT(FTRP(bp), PACK(size, 0));	// 设置脚部
	PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));	// 新的结尾块
		
	// 合并空闲
	return coalesce(bp);
} 

coalesce

static void *coalesce(void *bp){
	// 找到前一个块,取头部或尾部序言,取分配位
	size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp)));
	size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
	// 取块大小
	size_t size = GET_SIZE(HDRP(bp));
	
	// 前后都有不需要合并
	if(prev_alloc && next_alloc){
		return bp;
	}
	// 前有后无
	else if(prev_alloc && !next_alloc){	
		size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
		PUT(HDRP(bp), PACK(size, 0));
		PUT(FTRP(bp), PACK(size, 0));
	}
	// 前无后有
	else if(!prev_alloc && next_alloc){	
		size += GET_SIZE(HDRP(PREV_BLKP(bp)));
		PUT(FTRP(bp), PACK(size, 0));
		PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
		bp = PREV_BLKP(bp);
	}
	// 前无后无
	else{
		size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(HDRP(NEXT_BLKP(bp)));
		PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0));
		PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0));
		bp = PREV_BLKP(bp);
	}
	return bp;
}

 mm_malloc

void *mm_malloc(size_t size)
{
	size_t asize;
	void *bp;
	size_t extendsize;
	
	if(size == 0) return NULL;
	
	// 加上开销字节,然后对齐, 最小大小是双字
	if(size <= DSIZE){
		asize = 2 * DSIZE;
	}
	else{
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}
	
	// 寻找合适的空闲块
	if((bp = first_fit(asize)) != NULL){
		place(bp, asize);
		return bp;
	}
	
	// 没找到
	extendsize = MAX(asize, CHUNKSIZE);
	if((bp = extend_heap(extendsize / WSIZE)) == NULL){
		return NULL;
	}
	place(bp, asize);
	return bp;
}

 first_fit

static void *first_fit(size_t asize){
	void *bp = heap_listp;
	size_t size;
	while((size = GET_SIZE(HDRP(bp))) != 0){
		if(size >= asize && !GET_ALLOC(HDRP(bp))){
			return bp;
		}
		bp = NEXT_BLKP(bp);
	}
	return NULL;
}

 best_fit

static void *best_fit(size_t asize){
	void *bp = heap_listp;
	size_t size;
	void *best = NULL;
	size_t minSize = 0;
	
	while((size = GET_SIZE(HDRP(bp))) != 0){
		
		if(size >= asize && !GET_ALLOC(HDRP(bp)) && (minSize == 0 || minSize > size)){
			best = bp;
			minSize = size;
			if(size == asize){
				return best;
			}
		}
		bp = NEXT_BLKP(bp);
	}
	return best;
}

 place

static void place(void *bp, size_t asize){
	size_t reSize = GET_SIZE(HDRP(bp)) - asize;
	if(reSize >= DSIZE){
		PUT(HDRP(bp), PACK(asize, 1));
		PUT(FTRP(bp), PACK(asize, 1));
		PUT(HDRP(NEXT_BLKP(bp)), PACK(reSize, 0));
		PUT(FTRP(NEXT_BLKP(bp)), PACK(reSize, 0));
	}
	else{
		PUT(HDRP(bp), PACK(GET_SIZE(HDRP(bp)), 1));
		PUT(FTRP(bp), PACK(GET_SIZE(HDRP(bp)), 1));		
	}
}

 mm_free

void mm_free(void *ptr){
	// 获得序言块,序言块中提取出块的大小
	size_t size = GET_SIZE(HDRP(ptr));
	
	// 设置为未分配
	PUT(HDRP(ptr), PACK(size, 0));
	PUT(FTRP(ptr), PACK(size, 0));
	// 合并
	coalesce(ptr);
}

 mm_realloc

void *mm_realloc(void *ptr, size_t size){
	size_t asize, ptrSize;
	void *newBp;
	
	// 空的直接申请新的
	if(ptr == NULL){
		return mm_malloc(size);
	}
	// 改为0则相当于直接删
	if(size == 0){
		mm_free(ptr);
		return NULL;
	}
	
	// 对齐修改的大小
	if(size <= DSIZE){
		asize = 2 * DSIZE;
	}
	else{
		asize = DSIZE * ((size + (DSIZE) + (DSIZE - 1)) / DSIZE);
	}
	
	// 看当前块前后是否有空白,有则合并然后看当前块大小
	newBp = coalesce(ptr);
	ptrSize = GET_SIZE(HDRP(newBp));
	// 先标记为使用状态
	PUT(HDRP(newBp), PACK(ptrSize, 1));
	PUT(FTRP(newBp), PACK(ptrSize, 1));
	
	// 检查是否有合并前面的块(合并后面的不用改起始位置),前移
	if(newBp != ptr){
		memcpy(newBp, ptr, GET_SIZE(HDRP(ptr)) - DSIZE);
	}
	
	// 当前块大小和目标块大小一样,直接返回
	if(ptrSize == asize){
		return newBp;
	}
	// 当前块比目标大,切割
	else if(ptrSize > asize){
		place(newBp, asize);
		return newBp;
	}
	// 当前块比目标小,拷贝到新的
	else{
		ptr = mm_malloc(asize);
		if(ptr == NULL){
			return NULL;
		}
		memcpy(ptr, newBp, ptrSize - DSIZE);
		mm_free(newBp);
		return ptr;
	}
}

3、总结

懵懵懂懂,请多指教!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值