注意:head 域和 foot 域在本节中都假设只占用当前存储块的 1 个存储单位的空间,对于该结点整个存储空间来说,可以忽略不计。也就是说,在可利用空间表中,知道下一个结点的首地址,该值减 1 就可以找到当前结点的 foot 域。使用边界标识法的可利用空间表本身是双向循环链表,每个内存块结点都有指向前驱和后继结点的指针域。
typedef struct WORD{ union{ struct WORD *llink;//指向直接前驱 struct WORD *uplink;//指向结点本身 }; int tag;//标记域,0表示为空闲块;1表示为占用块 int size;//记录内存块的存储大小 struct WORD *rlink;//指向直接后继 OtherType other;//内存块可能包含的其它的部分 }WORD,head,foot,*Space;
针对这种情况,解决的措施是:3 种分配方法分别为:首部拟合法、最佳拟合法和最差拟合法。
Space AllocBoundTag(Space *pav,int n){ Space p,f; int e=10;//设定常亮 e 的值 //如果在遍历过程,当前空闲块的在存储容量比用户申请空间 n 值小,在该空闲块有右孩子的情况下直接跳过 for (p=(*pav); p&&p->size<n&&p->rlink!=(*pav); p=p->rlink); //跳出循环,首先排除p为空和p指向的空闲块容量小于 n 的情况 if (!p ||p->size<n) { return NULL; }else{ //指针f指向p空闲块的foot域 f=FootLoc(p); //调整pav指针的位置,为下次分配做准备 (*pav)=p->rlink; //如果该空闲块的存储大小比 n 大,比 n+e 小,负责第一种情况,将 p 指向的空闲块全部分配给用户 if (p->size-n <= e) { if ((*pav)==p) { pav=NULL; } else{ //全部分配用户,即从可利用空间表中删除 p 空闲块 (*pav)->llink=p->llink; p->llink->rlink=(*pav); } //同时调整head域和foot域中的tag值 p->tag=f->tag=1; } //否则,从p空闲块中拿出 大小为 n 的连续空间分配给用户,同时更新p剩余存储块中的信息。 else{ //更改分配块foot域的信息 f->tag=1; p->size-=n; //f指针指向剩余空闲块 p 的底部 f=FootLoc(p); f->tag=0; f->uplink=p; p=f+1;//p指向的是分配给用户的块的head域,也就是该块的首地址 p->tag=1; p->size=n; } return p; } }
判断当前空闲块左右两侧是否为空闲块的方法是:对于当前空闲块 p ,p-1 就是相邻的低地址处的空闲块的 foot 域,如果 foot 域中的 tag 值为 0 ,表明其为空闲块; p+p->size 表示的是高地址处的块的 head 域,如果 head 域中的 tag 值为 0,表明其为空闲块。
//设定p指针指向的为用户释放的空闲块 p->tag=0; //f指针指向p空闲块的foot域 Space f=FootLoc(p); f->uplink=p; f->tag=0; //如果pav指针不存在,证明可利用空间表为空,此时设置p为头指针,并重新建立双向循环链表 if (!pav) { pav=p->llink=p->rlink=p; }else{ //否则,在p空闲块插入到pav指向的空闲块的左侧 Space q=pav->llink; p->rlink=pav; p->llink=q; q->rlink=pav->llink=p; pav=p; }如果该空闲块的左侧相邻的块为空闲块,右侧为占用块,处理的方法是:只需要更改左侧空闲块中的 size 的大小,并重新设置左侧空闲块的 foot 域即可(如图 2)。
//常量 n 表示当前空闲块的存储大小 int n=p->size; Space s=(p-1)->uplink;//p-1 为当前块的左侧块的foot域,foot域中的uplink指向的就是左侧块的首地址,s指针代表的是当前块的左侧存储块 s->size+=n;//设置左侧存储块的存储容量 Space f=p+n-1;//f指针指向的是空闲块 p 的foot域 f->uplink=s;//这是foot域的uplink指针重新指向合并后的存储空间的首地址 f->tag=0;//设置foot域的tag标记为空闲块如果用户释放的内存块的相邻左侧为占用块,右侧为空闲块,处理的方法为:将用户释放掉的存储块替换掉右侧的空闲块,同时更改存储块的 size 和右侧空闲块的 uplink 指针的指向(如图 3 所示)。
Space t=p+p->size;//t指针指向右侧空闲块的首地址 p->tag=0;//初始化head域的tag值为0 //找到t右侧空闲块的前驱结点和后继结点,用当前释放的空闲块替换右侧空闲块 Space q=t->llink; p->llink=q; q->rlink=p; Space q1=t->rlink; p->rlink=q1; q1->llink=p; //更新释放块的size的值 p->size+=t->size; //更改合并后的foot域的uplink指针的指向 Space f=FootLoc(t); f->uplink=p;
实现代码为:此情况和只有左侧有空闲块的情况雷同,唯一的不同点是多了一步摘除右侧相邻空闲块结点的操作。
int n=p->size; Space s=(p-1)->uplink;//找到释放内存块物理位置相邻的低地址的空闲块 Space t=p+p->size;//找到物理位置相邻的高地址处的空闲块 s->size+=n+t->size;//更新左侧空闲块的size的值 //从可利用空间表中摘除右侧空闲块 Space q=t->llink; Space q1=t->rlink; q->rlink=q1; q1->llink=q; //更新合并后的空闲块的uplink指针的指向 Space f=FootLoc(t); f->uplink=s;
Copyright © 广州京杭网络科技有限公司 2005-2024 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有