mirror of
https://github.com/kangjianwei/Data-Structure.git
synced 2026-02-06 00:07:05 +08:00
💡 动态存储管理源码重构
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Space pav;
|
||||
Space p[12]; // 记录申请到的内存的指针
|
||||
int s[12] = {10, 20, 30, 50, 5, 15, 10, 5, 15, 15, 2, 20}; // 申请的空间大小
|
||||
int i = 0;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
int max = 200; // 初值建议为20的倍数,目的是打印出来可以对齐
|
||||
|
||||
printf("初始化包含 %d 个\"字\"的内存块后,当前内存布局为:\n", max);
|
||||
pav = InitSpace(max);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBoundTag \n");
|
||||
{
|
||||
for(i = 0; i < 12; i++) {
|
||||
printf("████ %2d> 申请 %d 个\"字\"的内存后,当前内存布局为:\n", i + 1, s[i]);
|
||||
p[i] = AllocBoundTag(&pav, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBoundTag \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[10] = {7, 3, 10, 6, 8, 5, 11, 1, 0, 4};
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
printf("回收 p%d 指向的内存...\n", a[i] + 1);
|
||||
FreeBoundTag(&pav, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
330
CLion/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.c
Normal file
330
CLion/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
/*
|
||||
* 全局变量:永远指向初始空间的头部。
|
||||
* 该变量有两个作用:
|
||||
* 1.用来追踪内存布局,即查看内存的使用情况。
|
||||
* 2.指示内存的起始和结尾
|
||||
*/
|
||||
static Space av;
|
||||
|
||||
// 记录空间容量,用于追踪内存布局
|
||||
static int len;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块总大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n) {
|
||||
Space space, head, foot;
|
||||
|
||||
// 初始化空闲内存
|
||||
space = (Space) malloc(n * sizeof(WORD));
|
||||
if(space == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 初始化头部信息
|
||||
head = space;
|
||||
head->llink = space; // 前驱指向自身
|
||||
head->rlink = space; // 后继指向自身
|
||||
head->size = n; // 空间大小,已经包含了head和foot
|
||||
head->tag = 0; // 标记空间空闲
|
||||
|
||||
// 初始化底部信息
|
||||
foot = FootLoc(head); // 初始化底部指针
|
||||
foot->uplink = head; // 底部域链接到本结点的头部
|
||||
foot->tag = 0; // 标记空间空闲
|
||||
|
||||
// 记下空间的起始位置和容量
|
||||
av = space;
|
||||
len = n;
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n) {
|
||||
Space p, f;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于3,则直接返回。
|
||||
* 因为head和foot本身就占了2个字空间,用户至少申请1个字的话,总申请空间至少为3个字。
|
||||
*/
|
||||
if(n < 3) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于3\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(p = *pav; p && p->size < n && p->rlink != *pav; p = p->rlink) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(!p || p->size < n) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 至此,p指向了满足条件的空闲块 */
|
||||
|
||||
// 让f指向该空闲块的底部
|
||||
f = FootLoc(p);
|
||||
|
||||
// pav指向p结点的后继结点,即指向下一个空闲块
|
||||
*pav = p->rlink;
|
||||
|
||||
// 如果空闲块比申请的容量大不了多少,则需要整块分配,即不保留<=e的剩余量
|
||||
if(p->size - n <= e) {
|
||||
// 由于上面已经让pav指向了下一个空闲块,所以如果pav与p相等,说明此时只有一个空闲块了(注意空闲块链表是双循环的)
|
||||
if(*pav == p) {
|
||||
*pav = NULL; // 如果仅剩一个空闲块了,那么被占用后,可利用空间表变为空表
|
||||
|
||||
// 否则,在表中删除分配的结点
|
||||
} else {
|
||||
(*pav)->llink = p->llink; // 修改pav的前驱
|
||||
p->llink->rlink = *pav; // 修改pav前驱的后继
|
||||
|
||||
/* 在上述操作中,p结点的前驱与后继并没有改变,这是为了方便将来的回收操作 */
|
||||
}
|
||||
|
||||
// 更新占用块为占用状态
|
||||
p->tag = f->tag = 1;
|
||||
|
||||
printf("日志:分配成功!申请 %d 个\"字\",实际分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, n + e, AvailableSpace(*pav));
|
||||
|
||||
// 如果空闲块很大,则从中间切割,占用后面的n个字
|
||||
} else {
|
||||
f->tag = 1; // 修改分配块的底部标志,表示其处于占用状态
|
||||
p->size -= n; // 设置剩余块大小
|
||||
|
||||
f = FootLoc(p); // 计算剩余块的新底部位置
|
||||
f->tag = 0; // 设置新底部标志为空闲
|
||||
f->uplink = p; // 新底部依然指向空闲块头部
|
||||
|
||||
p = f + 1; // 计算分配块的新头部位置
|
||||
p->tag = 1; // 设置分配块新头部标志为占用
|
||||
p->size = n; // 设置分配块的容量
|
||||
|
||||
// 修改分配块底部的链接(教材中缺失了此步骤)
|
||||
(FootLoc(p))->uplink = p;
|
||||
|
||||
printf("日志:分配成功!申请并分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, AvailableSpace(*pav));
|
||||
}
|
||||
|
||||
// 返回分配块首地址
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:以下描述中的"释放块"就是指针p指向的内存
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p) {
|
||||
Space h, f, q;
|
||||
int Ltag, Rtag;
|
||||
|
||||
if(p == NULL) {
|
||||
printf("日志:回收失败!内存指针为空。空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
return;
|
||||
}
|
||||
|
||||
Ltag = p == av ? 1 : (p - 1)->tag; // 块p的左邻区标志。特别注意,如果块p位于内存起始处,则认为它的左邻区为占用。
|
||||
Rtag = (p + p->size) == (av + len) ? 1 : (p + p->size)->tag; // 块p的右邻区标志。特别注意,如果块p位于内存结尾处,则认为它的右邻区为占用。
|
||||
|
||||
/*
|
||||
* 1.释放块的左、右邻区均为占用块
|
||||
*
|
||||
* 此时仅需要将块p插入到pav所指结点的之前即可(当然,插入到之后也是可以的)
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为占用块...\n", p->size);
|
||||
|
||||
f = FootLoc(p);
|
||||
f->uplink = p;
|
||||
f->tag = 0;
|
||||
|
||||
p->tag = 0;
|
||||
|
||||
// 空闲链表为空时,直接将块p变为新的独立的空闲块
|
||||
if((*pav) == NULL) {
|
||||
*pav = p->llink = p->rlink = p;
|
||||
|
||||
// 否则,将块p插入到pav之前
|
||||
} else {
|
||||
q = (*pav)->llink;
|
||||
p->rlink = *pav;
|
||||
p->llink = q;
|
||||
q->rlink = (*pav)->llink = p;
|
||||
|
||||
// 令刚释放的结点成为下次分配空间时最先查找的结点
|
||||
*pav = p;
|
||||
}
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2.释放块的左邻区为空闲块,右邻区为占用块
|
||||
*
|
||||
* 此时需要合并左邻区与释放块
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为空闲块,右邻区为占用块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
h->size += p->size; // 左邻区容量增大
|
||||
|
||||
f = FootLoc(p); // 将释放块的底部做为合并后的新块的底部
|
||||
f->uplink = h;
|
||||
f->tag = 0;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3.释放块的左邻区为占用块,右邻区为空闲块
|
||||
*
|
||||
* 此时需要合并释放块与右邻区
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为占用块,右邻区为空闲块...\n", p->size);
|
||||
|
||||
h = p + p->size; // 右邻区的头部
|
||||
|
||||
f = FootLoc(h); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
f->uplink = p; // 释放块的头部将作为合并后新块的头部
|
||||
|
||||
p->tag = 0;
|
||||
p->size += h->size;
|
||||
|
||||
// 释放块的头部链接域要更新为与右邻区头部的链接域一致
|
||||
p->llink = h->llink;
|
||||
p->rlink = h->rlink;
|
||||
h->llink->rlink = p;
|
||||
h->rlink->llink = p;
|
||||
|
||||
// pav指向合并后的结点的新头部
|
||||
*pav = p;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4.释放块的左、右邻区均为空闲块
|
||||
*
|
||||
* 此时需要合并左邻区、释放块、右邻区
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为空闲块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
q = p + p->size; // 右邻区的头部
|
||||
f = FootLoc(q); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
|
||||
h->size += p->size + q->size; // 合并后的新块大小
|
||||
f->uplink = h; // 新块底部信息也要更新
|
||||
|
||||
// 移除右邻区
|
||||
q->rlink->llink = q->llink;
|
||||
q->llink->rlink = q->rlink;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
Space p;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
p = av;
|
||||
count = av->size;
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("□");
|
||||
} else {
|
||||
printf("■");
|
||||
}
|
||||
|
||||
if(i == count && count < len) {
|
||||
p = p + p->size;
|
||||
count += p->size;
|
||||
printf("|");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
// 每隔20个换一下行
|
||||
if(i % 20 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(len % 20 != 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav) {
|
||||
Space p;
|
||||
int count;
|
||||
|
||||
if(pav == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = pav;
|
||||
count = 0;
|
||||
|
||||
do {
|
||||
count += p->size;
|
||||
p = p->rlink;
|
||||
} while(p != pav);
|
||||
|
||||
return count;
|
||||
}
|
||||
96
CLion/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.h
Normal file
96
CLion/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#ifndef BOUNDARYTAGMETHOD_H
|
||||
#define BOUNDARYTAGMETHOD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define e 5 // 分配空间时用到的容差
|
||||
#define FootLoc(p) p+(p->size)-1 // 将p指针定位到p所指内存块的底部
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 教材中将llink和uplink封装在了一个联合体中,个人感觉这个封装有些鸡肋。
|
||||
* 一方面,head和foot的公共部分其实只有tag,如果真想用联合体,那么应当把size和rlink也封装起来。
|
||||
* 另一方面,这个封装节省的空间很有限,而且直接影响了代码的可读性。
|
||||
* 这里只是教学代码,而不是真实的系统代码,所以空间考虑在其次,原理展示为首要任务。
|
||||
* 此外,教材中的伪码中也并没有考虑这个联合体,而是直接进行操作的。
|
||||
* 综上所述,这里去除了教材中的联合体结构。
|
||||
* 如想观察带有联合体的写法,可以参考CFree分支的代码。
|
||||
*/
|
||||
|
||||
int tag; // 块标志,0空闲,1占用,头部和尾部均有
|
||||
|
||||
struct WORD* llink; // 头部域,指向前驱结点
|
||||
struct WORD* rlink; // 头部域,指向后继结点
|
||||
int size; // 头部域,块大小
|
||||
|
||||
struct WORD* uplink; // 底部域,指向本结点头部
|
||||
} WORD;
|
||||
|
||||
typedef WORD* Space; // Space:指向可利用空间的指针类型
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n);
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav);
|
||||
|
||||
#endif
|
||||
12
CLion/CourseBook/0801_BoundaryTagMethod/CMakeLists.txt
Normal file
12
CLion/CourseBook/0801_BoundaryTagMethod/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(BoundaryTagMethod BoundaryTagMethod.h BoundaryTagMethod.c BoundaryTagMethod-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(BoundaryTagMethod Scanf_lib)
|
||||
|
||||
# 记录要拷贝到*.exe目录下的资源文件
|
||||
file(GLOB TestData TestData*.txt)
|
||||
# 将资源文件拷贝到*.exe目录下,不然无法加载
|
||||
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
44
CLion/CourseBook/0802_BuddySystem/BuddySystem-main.c
Normal file
44
CLion/CourseBook/0802_BuddySystem/BuddySystem-main.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include "BuddySystem.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
FreeList avail;
|
||||
WORD* p[8]; // 记录申请到的内存的指针
|
||||
int s[8] = {4, 5, 6, 7, 1, 5, 3, 9}; // 申请的空间大小
|
||||
int i;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
printf("初始化一个内存块...\n");
|
||||
InitSpace(avail);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBuddy \n");
|
||||
{
|
||||
for(i = 0; i < 8; i++) {
|
||||
printf("申请大小为 %d 个字的内存块...\n", s[i]);
|
||||
p[i] = AllocBuddy(avail, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBuddy \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[8] = {2, 0, 5, 7, 1, 4, 3, 6};
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
FreeBuddy(avail, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
283
CLion/CourseBook/0802_BuddySystem/BuddySystem.c
Normal file
283
CLion/CourseBook/0802_BuddySystem/BuddySystem.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#include "BuddySystem.h"
|
||||
|
||||
// 记录内存的起始地址,在计算伙伴块时候需要用到
|
||||
WORD* start;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail) {
|
||||
int k;
|
||||
WORD* r;
|
||||
|
||||
// 遍历M+1个元素
|
||||
for(k = 0; k <= M; k++) {
|
||||
avail[k].nodesize = (int) pow(2, k);
|
||||
avail[k].first = NULL;
|
||||
}
|
||||
|
||||
r = (WORD*) malloc((int) pow(2, M) * sizeof(WORD));
|
||||
if(r == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 设置头部信息
|
||||
r->llink = r->rlink = r;
|
||||
r->tag = 0;
|
||||
r->kval = M;
|
||||
|
||||
avail[M].first = r;
|
||||
|
||||
start = r;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n) {
|
||||
int k, i;
|
||||
WORD* pa, * pre, * suc, * pi;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于1,则直接返回。
|
||||
*/
|
||||
if(n < 1) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于1\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(k = 0; k <= M && (avail[k].nodesize < n + 1 || !avail[k].first); k++) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(k > M) {
|
||||
printf("日志:分配失败!没有足够的空闲块\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa = avail[k].first; // 指向可分配子表的第一个结点
|
||||
pre = pa->llink; // 分别记下前驱和后继
|
||||
suc = pa->rlink;
|
||||
|
||||
// 如果此处仅有一个空闲块,则分配后该子表变为空
|
||||
if(pa == suc) {
|
||||
avail[k].first = NULL;
|
||||
|
||||
// 否则,从链表头部摘下一个可用的空闲块,并将子表头指针指向下一个空闲块
|
||||
} else {
|
||||
pre->rlink = suc;
|
||||
suc->llink = pre;
|
||||
avail[k].first = suc;
|
||||
}
|
||||
|
||||
/*
|
||||
* 从k-1处开始逆向遍历FreeList数组,向其中填充剩余的空闲块。
|
||||
* 剩余的空闲块是对剩余可用空间的拆分。
|
||||
*
|
||||
* 这里用到一个公式:2^m-2^n = 2^n+2^(n+1)+...+2^(m-1)
|
||||
* 比如初始容量为2^16,此时总共申请1500个字,那么需要分配一块2^11的空闲块给它。
|
||||
* 分配完之后,剩余的容量为:2^16-2^11 = 2^11+2^12+2^13+2^14+2^15。
|
||||
* 这些剩余容量可以拆分为5个空闲块,分别存储到15、14、13、12、11这五个索引处。
|
||||
*/
|
||||
for(i = 1; k - i >= 0 && avail[k - i].nodesize >= n + 1; i++) {
|
||||
pi = pa + (int) pow(2, k - i); // 每次将pi指向剩余空间的后一半
|
||||
pi->rlink = pi->llink = pi; // 初始化pi的前驱和后继
|
||||
pi->tag = 0; // 标记为空闲块
|
||||
pi->kval = k - i; // 设置该块的容量标志,真实容量为2^(k-i)
|
||||
avail[k - i].first = pi;
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 上面分解出来的pi直接添加到了avail中,并没有考虑同位置处会不会有别的容量相同的空闲块。
|
||||
* 这里不需要考虑的原因是如果同位置处已经存在别的容量相同的空闲块,
|
||||
* 那么这里根本不需要分解剩余空间,换句话说,连这个循环都进不来。
|
||||
* 只要进来这个循环,说明目标位置处已经为空了,没有找到合适的空闲块,所以这才进一步向高游标处寻找空闲块。
|
||||
*/
|
||||
}
|
||||
|
||||
// 最后剩下的最靠前的空间就是需要分配的空间(这里没有设置pa的前驱和后继,因为没必要)
|
||||
pa->tag = 1;
|
||||
pa->kval = k - (--i);
|
||||
|
||||
printf("日志:分配成功!用户申请 %d 个字,系统申请 %d 个字,实际分配 %d 个字\n", n, n + 1, (int) pow(2, pa->kval));
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p) {
|
||||
int k;
|
||||
WORD* r;
|
||||
WORD* buddy = Buddy(p);
|
||||
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 这里将p结点简单地插入到avail中,包含三种情形:
|
||||
* 1.伙伴块非空闲
|
||||
* 2.伙伴空闲,但是伙伴的大小跟p的大小不一致,说明伙伴还没拼合好
|
||||
* 3.p拼接成了最后一个最大的空闲块
|
||||
*/
|
||||
if(buddy->tag == 1 || buddy->kval != p->kval || p->kval == M) {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找p结点应当进入的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,采用头插法将空闲块插入到目标插槽
|
||||
if(k <= M && k == p->kval) {
|
||||
p->tag = 0;
|
||||
|
||||
if(avail[k].first == NULL) {
|
||||
p->llink = p->rlink = p;
|
||||
} else {
|
||||
p->llink = avail[k].first->llink;
|
||||
p->rlink = avail[k].first;
|
||||
p->llink->rlink = p;
|
||||
p->rlink->llink = p;
|
||||
}
|
||||
|
||||
avail[k].first = p;
|
||||
|
||||
printf("日志:回收成功![%d, (2^%d)]进入插槽 %d 的空闲块链表上\n", (int) (p - start), k, k);
|
||||
}
|
||||
|
||||
// 如果伙伴块是空闲的,此时应当进行合并操作
|
||||
} else {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找伙伴块所在的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,将伙伴块从空闲块链表中摘下来
|
||||
if(k <= M && k == p->kval) {
|
||||
// 伙伴在链表第一个位置
|
||||
if(avail[k].first == buddy) {
|
||||
buddy->rlink->llink = buddy->llink;
|
||||
buddy->llink->rlink = buddy->rlink;
|
||||
|
||||
avail[k].first = buddy->rlink;
|
||||
|
||||
// 伙伴在中间位置
|
||||
} else {
|
||||
for(r = avail[k].first; r->rlink != buddy; r = r->rlink) {
|
||||
// 查找伙伴,r指向伙伴的前驱
|
||||
}
|
||||
|
||||
r->rlink = buddy->rlink;
|
||||
buddy->rlink->llink = r;
|
||||
}
|
||||
|
||||
printf("日志:合并成功![%d, (2^%d)]和[%d, (2^%d)]合并成了", (int) (p - start), k, (int) (buddy - start), k);
|
||||
|
||||
// 合并之前,需要确定哪个伙伴靠前
|
||||
if(p < buddy) {
|
||||
p->tag = 0;
|
||||
} else {
|
||||
p = buddy;
|
||||
}
|
||||
|
||||
p->kval = k + 1; // 指数增一后,即完成合并
|
||||
|
||||
printf("[%d, (2^%d)]\n", (int) (p - start), k + 1);
|
||||
|
||||
// 出现新的空闲块之后,要进入递归,查看该空闲块是否也存在空闲伙伴
|
||||
FreeBuddy(avail, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
int i, count, total;
|
||||
WORD* p;
|
||||
|
||||
printf("|");
|
||||
|
||||
p = start;
|
||||
count = (int) pow(2, p->kval);
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("_");
|
||||
} else {
|
||||
printf("*");
|
||||
}
|
||||
|
||||
// 进入到下一个块
|
||||
if(i == count && count < (int) pow(2, M)) {
|
||||
p = start + count;
|
||||
count += (int) pow(2, p->kval);
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p) {
|
||||
long s, m, n;
|
||||
|
||||
if(p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start是整个空闲块的绝对起始地址,s是p在伙伴系统中的绝对地址,从0开始
|
||||
s = p - start;
|
||||
if(s < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m = (long) pow(2, p->kval);
|
||||
n = (long) pow(2, p->kval + 1);
|
||||
|
||||
if(s % n == 0) {
|
||||
return p + m;
|
||||
}
|
||||
|
||||
if(s % n == m) {
|
||||
return p - m;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
89
CLion/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
89
CLion/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#ifndef BUDDYSYSTEM_H
|
||||
#define BUDDYSYSTEM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define M 6 // 假设总空间大小为2^M个字,子表个数为M+1
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
struct WORD* llink; // 头部域的前驱指针
|
||||
struct WORD* rlink; // 头部域的后继指针
|
||||
int tag; // 头部域的块标志,0:空闲,1:占用
|
||||
int kval; // 指示块的大小,比如其值为K时,表示该块的大小为2^K
|
||||
} WORD;
|
||||
|
||||
// 表头向量类型
|
||||
typedef struct HeadNode {
|
||||
int nodesize; // 该链表的空闲块的大小
|
||||
WORD* first; // 该链表的表头指针
|
||||
} FreeList[M + 1];
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n);
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p);
|
||||
|
||||
#endif
|
||||
12
CLion/CourseBook/0802_BuddySystem/CMakeLists.txt
Normal file
12
CLion/CourseBook/0802_BuddySystem/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(BuddySystem BuddySystem.h BuddySystem.c BuddySystem-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(BuddySystem Scanf_lib)
|
||||
|
||||
# 记录要拷贝到*.exe目录下的资源文件
|
||||
file(GLOB TestData TestData*.txt)
|
||||
# 将资源文件拷贝到*.exe目录下,不然无法加载
|
||||
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
12
CLion/CourseBook/0803_GarbageCollection/CMakeLists.txt
Normal file
12
CLion/CourseBook/0803_GarbageCollection/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# 包含公共库
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Status)
|
||||
|
||||
# 生成可执行文件
|
||||
add_executable(GarbageCollection SString.h SString.c GList-HT.h GList-HT.c GarbageCollection.h GarbageCollection.c GarbageCollection-main.c)
|
||||
# 链接公共库
|
||||
target_link_libraries(GarbageCollection Scanf_lib)
|
||||
|
||||
# 记录要拷贝到*.exe目录下的资源文件
|
||||
file(GLOB TestData TestData*.txt)
|
||||
# 将资源文件拷贝到*.exe目录下,不然无法加载
|
||||
file(COPY ${TestData} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
201
CLion/CourseBook/0803_GarbageCollection/GList-HT.c
Normal file
201
CLion/CourseBook/0803_GarbageCollection/GList-HT.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#include "GList-HT.h" //**▲05 数组和广义表**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L) {
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*L = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S) {
|
||||
SString emp; // 代表空广义表的字符串
|
||||
SString hsub, sub;
|
||||
GList p, q;
|
||||
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 清理字符串S中的空白,包括清理不可打印字符和清理空格
|
||||
ClearBlank(S);
|
||||
|
||||
if(StrEmpty(S)) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
StrAssign(emp, "()");
|
||||
|
||||
/*
|
||||
* 如果输入串为(),则代表需要创建空的广义表
|
||||
*
|
||||
*【注】
|
||||
* 教材这里的代码是有问题的。
|
||||
* StrCompare的返回值指示的是两个字符串的大小,而不是指示两个字符串是否相等。
|
||||
* 如果给定的S与()相等,返回值应当是0。
|
||||
*/
|
||||
if(!StrCompare(S, emp)) {
|
||||
*L = NULL;
|
||||
} else {
|
||||
*L = (GList) malloc(sizeof(GLNode));
|
||||
if(*L == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
(*L)->mark = 0;
|
||||
|
||||
// 创建原子
|
||||
if(StrLength(S) == 1) {
|
||||
(*L)->tag = Atom;
|
||||
(*L)->Node.atom = S[1];
|
||||
} else {
|
||||
(*L)->tag = List;
|
||||
|
||||
p = *L;
|
||||
|
||||
// 去掉最外层括号
|
||||
SubString(sub, S, 2, StrLength(S) - 2);
|
||||
|
||||
// 重复建n个子表
|
||||
do {
|
||||
// 从sub中分离出表头串hsub,分离完成后,sub也会发生变化
|
||||
sever(hsub, sub);
|
||||
|
||||
// 递归创建广义表
|
||||
CreateGList(&(p->Node.ptr.hp), hsub);
|
||||
|
||||
q = p;
|
||||
|
||||
// 如果表尾不为空,需要继续处理表尾
|
||||
if(!StrEmpty(sub)) {
|
||||
p = (GList) malloc(sizeof(GLNode));
|
||||
if(p == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
p->mark = 0;
|
||||
|
||||
p->tag = List;
|
||||
|
||||
q->Node.ptr.tp = p;
|
||||
}
|
||||
} while(!StrEmpty(sub));
|
||||
|
||||
q->Node.ptr.tp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L) {
|
||||
Print(L, Head);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark) {
|
||||
// L为空
|
||||
if(L == NULL) {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
}
|
||||
|
||||
printf(")");
|
||||
|
||||
// L不为空时
|
||||
} else {
|
||||
// 对于原子结点,输出原子
|
||||
if(L->tag == Atom) {
|
||||
printf("%c", L->Node.atom);
|
||||
|
||||
// 对于子表结点,要对表头、表尾分别讨论
|
||||
} else {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
} else {
|
||||
printf(",");
|
||||
}
|
||||
|
||||
Print(L->Node.ptr.hp, Head);
|
||||
Print(L->Node.ptr.tp, Tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str) {
|
||||
int i, k, n;
|
||||
SString ch;
|
||||
|
||||
n = StrLength(str);
|
||||
|
||||
i = 0; // 遍历字符串时的游标
|
||||
k = 0; // 标记遇到的未配对括号数量
|
||||
|
||||
do {
|
||||
++i;
|
||||
|
||||
// 截取str第一个字符
|
||||
SubString(ch, str, i, 1);
|
||||
|
||||
if(ch[1] == '(') {
|
||||
++k;
|
||||
}
|
||||
|
||||
if(ch[1] == ')') {
|
||||
--k;
|
||||
}
|
||||
} while(i < n && (ch[1] != ',' || k != 0));
|
||||
|
||||
// 如果存在多个广义表结点
|
||||
if(i < n) {
|
||||
SubString(hstr, str, 1, i - 1);
|
||||
SubString(str, str, i + 1, n - i);
|
||||
|
||||
// 只有一个广义表结点
|
||||
} else {
|
||||
StrCopy(hstr, str);
|
||||
ClearString(str);
|
||||
}
|
||||
}
|
||||
96
CLion/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
96
CLion/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#ifndef GLIST_HT_H
|
||||
#define GLIST_HT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供 malloc、realloc、free、exit 原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "SString.h" //**▲04 串**//
|
||||
|
||||
/* 原子元素类型 */
|
||||
typedef char AtomType;
|
||||
|
||||
/*
|
||||
* 广义表结点标记
|
||||
*
|
||||
* Atom-0:原子结点
|
||||
* List-1:表结点
|
||||
*/
|
||||
typedef enum { Atom, List } ElemTag;
|
||||
|
||||
/* 广义表(头尾链表存储表示)类型定义 */
|
||||
typedef struct GLNode {
|
||||
int mark; // 为第8章第5节新增的变量,用来给广义表打上遍历标记,会在创建中初始化为0
|
||||
|
||||
ElemTag tag; // 公共标记,用于区分原子结点和表结点
|
||||
|
||||
// 原子结点和表结点的联合部分
|
||||
union {
|
||||
AtomType atom; // atom是原子结点的值域,AtomType由用户定义
|
||||
struct {
|
||||
struct GLNode* hp; // 指向表头
|
||||
struct GLNode* tp; // 指向表尾
|
||||
} ptr; // 表结点的指针域
|
||||
} Node;
|
||||
} GLNode;
|
||||
|
||||
/* 广义表类型 */
|
||||
typedef GLNode* GList;
|
||||
|
||||
/*
|
||||
* 图形化输出标记
|
||||
*
|
||||
* Head代表广义表指针来自表头
|
||||
* Tail代表广义表指针来自表尾
|
||||
*/
|
||||
typedef enum { Head, Tail } Mark;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S);
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L);
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <stdio.h>
|
||||
#include "GarbageCollection.h" //**▲08 动态存储管理**//
|
||||
|
||||
// 遍历广义表
|
||||
void Traverse(GList L, void(Visit)(GList));
|
||||
|
||||
// 打印广义表结点信息
|
||||
void PrintInfo(GList L);
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
GList G;
|
||||
|
||||
printf("创建并输出广义表:");
|
||||
{
|
||||
SString S;
|
||||
char* s = "((),(e),(a,(b,c,d)))";
|
||||
|
||||
InitGList(&G);
|
||||
|
||||
StrAssign(S, s);
|
||||
CreateGList(&G, S);
|
||||
|
||||
PrintGList(G);
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ MakeList \n");
|
||||
{
|
||||
printf("访问前的标志状态...\n");
|
||||
Traverse(G, PrintInfo);
|
||||
printf("\n");
|
||||
MakeList(G);
|
||||
|
||||
printf("访问后的标志状态...\n");
|
||||
Traverse(G, PrintInfo);
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// 遍历广义表
|
||||
void Traverse(GList L, void(Visit)(GList)) {
|
||||
if(L == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Visit(L);
|
||||
|
||||
if(L->tag == List) {
|
||||
Traverse(L->Node.ptr.hp, Visit);
|
||||
Traverse(L->Node.ptr.tp, Visit);
|
||||
}
|
||||
}
|
||||
|
||||
// 打印广义表结点信息
|
||||
void PrintInfo(GList L) {
|
||||
if(L->tag == Atom) {
|
||||
printf("mark = %d 原子结点:%c\n", L->mark, L->Node.atom);
|
||||
} else {
|
||||
printf("mark = %d 表结点\n", L->mark);
|
||||
}
|
||||
}
|
||||
78
CLion/CourseBook/0803_GarbageCollection/GarbageCollection.c
Normal file
78
CLion/CourseBook/0803_GarbageCollection/GarbageCollection.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*==============
|
||||
* 无用单元收集
|
||||
*
|
||||
* 包含算法: 8.3
|
||||
===============*/
|
||||
|
||||
#include "GarbageCollection.h"
|
||||
|
||||
/*
|
||||
* ████████ 算法8.3 ████████
|
||||
*
|
||||
* 遍历广义表(不使用栈)。
|
||||
*/
|
||||
void MakeList(GList G) {
|
||||
GList t, p, q;
|
||||
Status finished;
|
||||
|
||||
if(!G || G->mark != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
t = NULL; // t为p的母表
|
||||
p = G;
|
||||
finished = FALSE;
|
||||
|
||||
while(!finished) {
|
||||
while(p->mark == 0) {
|
||||
p->mark = 1; // MakeHead(p)的细化
|
||||
q = p->Node.ptr.hp; // q指向p的表头
|
||||
|
||||
if(q != NULL && q->mark == 0) {
|
||||
// 表头为原子结点
|
||||
if(q->tag == Atom) {
|
||||
q->mark = 1;
|
||||
|
||||
// 继续遍历子表
|
||||
} else {
|
||||
p->Node.ptr.hp = t;
|
||||
p->tag = Atom;
|
||||
t = p;
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
} // 完成对表头的标记
|
||||
|
||||
q = p->Node.ptr.tp;
|
||||
|
||||
// 继续遍历表尾
|
||||
if(q != NULL && q->mark == 0) {
|
||||
p->Node.ptr.tp = t;
|
||||
t = p;
|
||||
p = q;
|
||||
|
||||
// BackTrack(finished)的细化
|
||||
} else {
|
||||
// 表结点,从表尾回溯
|
||||
while(t && t->tag == List) {
|
||||
q = t;
|
||||
t = q->Node.ptr.tp;
|
||||
q->Node.ptr.tp = p;
|
||||
p = q;
|
||||
}
|
||||
|
||||
// 结束
|
||||
if(t == NULL) {
|
||||
finished = TRUE;
|
||||
|
||||
// 从表头回溯
|
||||
} else {
|
||||
q = t;
|
||||
t = q->Node.ptr.hp;
|
||||
q->Node.ptr.hp = p;
|
||||
p = q;
|
||||
p->tag = List;
|
||||
}// 继续遍历表尾
|
||||
}
|
||||
}
|
||||
}
|
||||
21
CLion/CourseBook/0803_GarbageCollection/GarbageCollection.h
Normal file
21
CLion/CourseBook/0803_GarbageCollection/GarbageCollection.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*==============
|
||||
* 无用单元收集
|
||||
*
|
||||
* 包含算法: 8.3
|
||||
===============*/
|
||||
|
||||
#ifndef GARBAGECOLLECTION_H
|
||||
#define GARBAGECOLLECTION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "GList-HT.h" //**▲05 数组和广义表**//
|
||||
|
||||
/*
|
||||
* ████████ 算法8.3 ████████
|
||||
*
|
||||
* 遍历广义表(不使用栈)。
|
||||
*/
|
||||
void MakeList(GList G);
|
||||
|
||||
#endif
|
||||
167
CLion/CourseBook/0803_GarbageCollection/SString.c
Normal file
167
CLion/CourseBook/0803_GarbageCollection/SString.c
Normal file
@@ -0,0 +1,167 @@
|
||||
/*=============================
|
||||
* 串的定长顺序存储表示(顺序串)
|
||||
*
|
||||
* 包含算法: 4.1、4.2、4.3、4.5
|
||||
==============================*/
|
||||
|
||||
#include "SString.h" //**▲04 串**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个值为chars的串T。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status StrAssign(SString T, const char* chars) {
|
||||
int i, len;
|
||||
|
||||
len = (int) strlen(chars);
|
||||
|
||||
// chars过长
|
||||
if(len > MAXSTRLEN) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
T[0] = len;
|
||||
for(i = 1; i <= len; i++) {
|
||||
T[i] = chars[i - 1];
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 清空
|
||||
*
|
||||
* 将串S清空。
|
||||
*/
|
||||
Status ClearString(SString S) {
|
||||
// 只需要将长度置为0就可以
|
||||
S[0] = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断串S中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 串S为空
|
||||
* FALSE: 串S不为空
|
||||
*/
|
||||
Status StrEmpty(SString S) {
|
||||
return S[0] == 0 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回串S中元素的个数。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrLength(SString S) {
|
||||
return S[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法4.3 ████████
|
||||
*
|
||||
* 求子串
|
||||
*
|
||||
* 用Sub返回S[pos, pos+len-1]。
|
||||
* 返回值指示是否截取成功。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status SubString(SString Sub, SString S, int pos, int len) {
|
||||
int i;
|
||||
|
||||
if(pos < 1 || pos > S[0] || len < 0 || pos + len - 1 > S[0]) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 复制元素
|
||||
for(i = 1; i <= len; i++) {
|
||||
Sub[i] = S[pos + i - 1];
|
||||
}
|
||||
|
||||
// 确定新长度
|
||||
Sub[0] = len;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 比较
|
||||
*
|
||||
* 比较串S和串T,返回比较结果。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrCompare(SString S, SString T) {
|
||||
int i = 1;
|
||||
|
||||
while(i <= S[0] && i <= T[0]) {
|
||||
// 遇到不同的字符时,比较其大小
|
||||
if(S[i] != T[i]) {
|
||||
return S[i] - T[i];
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return S[0] - T[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* 复制
|
||||
*
|
||||
* 将串S复制到串T。
|
||||
*/
|
||||
Status StrCopy(SString T, SString S) {
|
||||
int i;
|
||||
|
||||
// 连同长度信息一起复制
|
||||
for(i = 0; i <= S[0]; i++) {
|
||||
T[i] = S[i];
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 清理字符串S中的空白,包括清理不可打印字符和清理空格。
|
||||
*
|
||||
*【注】
|
||||
* 该函数是在本章中新增的。
|
||||
*/
|
||||
Status ClearBlank(SString S) {
|
||||
int i, j;
|
||||
|
||||
// 如果是空串
|
||||
if(S[0] == 0) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for(i = 1, j = 0; i <= S[0]; i++) {
|
||||
// 如果遇到空白,则略过
|
||||
if(S[i] == ' ' || !isprint(S[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
j++;
|
||||
|
||||
S[j] = S[i];
|
||||
}
|
||||
|
||||
S[0] = j;
|
||||
|
||||
return OK;
|
||||
}
|
||||
110
CLion/CourseBook/0803_GarbageCollection/SString.h
Normal file
110
CLion/CourseBook/0803_GarbageCollection/SString.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*=============================
|
||||
* 串的定长顺序存储表示(顺序串)
|
||||
*
|
||||
* 包含算法: 4.1、4.2、4.3、4.5
|
||||
==============================*/
|
||||
|
||||
#ifndef SSTRING_H
|
||||
#define SSTRING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> // 提供 strlen 原型
|
||||
#include <ctype.h> // 提供 isprint 原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define MAXSTRLEN 255 // 顺序串的最大串长
|
||||
|
||||
/*
|
||||
* 串的顺序存储类型定义
|
||||
*
|
||||
* 注:有效元素从SString的1号单元开始存储
|
||||
* SString的0号单元用来存储其长度
|
||||
*/
|
||||
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度
|
||||
|
||||
|
||||
/*
|
||||
* ████ 提示 ████
|
||||
*
|
||||
* 遵循教材的书写习惯,pos指示字符的位序(不是索引),从1开始计数
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个值为chars的串T。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status StrAssign(SString T, const char* chars);
|
||||
|
||||
/*
|
||||
* 清空
|
||||
*
|
||||
* 将串S清空。
|
||||
*/
|
||||
Status ClearString(SString S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断串S中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 串S为空
|
||||
* FALSE: 串S不为空
|
||||
*/
|
||||
Status StrEmpty(SString S);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回串S中元素的个数。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrLength(SString S);
|
||||
|
||||
/*
|
||||
* ████████ 算法4.3 ████████
|
||||
*
|
||||
* 求子串
|
||||
*
|
||||
* 用Sub返回S[pos, pos+len-1]。
|
||||
* 返回值指示是否截取成功。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status SubString(SString Sub, SString S, int pos, int len);
|
||||
|
||||
/*
|
||||
* 比较
|
||||
*
|
||||
* 比较串S和串T,返回比较结果。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrCompare(SString S, SString T);
|
||||
|
||||
/*
|
||||
* 复制
|
||||
*
|
||||
* 将串S复制到串T。
|
||||
*/
|
||||
Status StrCopy(SString T, SString S);
|
||||
|
||||
/*
|
||||
* 清理字符串S中的空白,包括清理不可打印字符和清理空格。
|
||||
*
|
||||
*【注】
|
||||
* 该函数是在本章中新增的。
|
||||
*/
|
||||
Status ClearBlank(SString S);
|
||||
|
||||
#endif
|
||||
@@ -57,3 +57,7 @@ add_subdirectory(0708_ArticulationPoints)
|
||||
add_subdirectory(0709_TopologicalSorting)
|
||||
add_subdirectory(0710_CriticalPathMethod)
|
||||
add_subdirectory(0711_ShortestPaths)
|
||||
|
||||
add_subdirectory(0801_BoundaryTagMethod)
|
||||
add_subdirectory(0802_BuddySystem)
|
||||
add_subdirectory(0803_GarbageCollection)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Space pav;
|
||||
Space p[12]; // 记录申请到的内存的指针
|
||||
int s[12] = {10, 20, 30, 50, 5, 15, 10, 5, 15, 15, 2, 20}; // 申请的空间大小
|
||||
int i = 0;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
int max = 200; // 初值建议为20的倍数,目的是打印出来可以对齐
|
||||
|
||||
printf("初始化包含 %d 个\"字\"的内存块后,当前内存布局为:\n", max);
|
||||
pav = InitSpace(max);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBoundTag \n");
|
||||
{
|
||||
for(i = 0; i < 12; i++) {
|
||||
printf("████ %2d> 申请 %d 个\"字\"的内存后,当前内存布局为:\n", i + 1, s[i]);
|
||||
p[i] = AllocBoundTag(&pav, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBoundTag \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[10] = {7, 3, 10, 6, 8, 5, 11, 1, 0, 4};
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
printf("回收 p%d 指向的内存...\n", a[i] + 1);
|
||||
FreeBoundTag(&pav, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
331
Dev-C++/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.cpp
Normal file
331
Dev-C++/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
/*
|
||||
* 全局变量:永远指向初始空间的头部。
|
||||
* 该变量有两个作用:
|
||||
* 1.用来追踪内存布局,即查看内存的使用情况。
|
||||
* 2.指示内存的起始和结尾
|
||||
*/
|
||||
static Space av;
|
||||
|
||||
// 记录空间容量,用于追踪内存布局
|
||||
static int len;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块总大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n) {
|
||||
Space space, head, foot;
|
||||
|
||||
// 初始化空闲内存
|
||||
space = (Space) malloc(n * sizeof(WORD));
|
||||
if(space == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 初始化头部信息
|
||||
head = space;
|
||||
head->llink = space; // 前驱指向自身
|
||||
head->rlink = space; // 后继指向自身
|
||||
head->size = n; // 空间大小,已经包含了head和foot
|
||||
head->tag = 0; // 标记空间空闲
|
||||
|
||||
// 初始化底部信息
|
||||
foot = FootLoc(head); // 初始化底部指针
|
||||
foot->uplink = head; // 底部域链接到本结点的头部
|
||||
foot->tag = 0; // 标记空间空闲
|
||||
|
||||
// 记下空间的起始位置和容量
|
||||
av = space;
|
||||
len = n;
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n) {
|
||||
Space p, f;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于3,则直接返回。
|
||||
* 因为head和foot本身就占了2个字空间,用户至少申请1个字的话,总申请空间至少为3个字。
|
||||
*/
|
||||
if(n < 3) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于3\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(p = *pav; p && p->size < n && p->rlink != *pav; p = p->rlink) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(!p || p->size < n) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 至此,p指向了满足条件的空闲块 */
|
||||
|
||||
// 让f指向该空闲块的底部
|
||||
f = FootLoc(p);
|
||||
|
||||
// pav指向p结点的后继结点,即指向下一个空闲块
|
||||
*pav = p->rlink;
|
||||
|
||||
// 如果空闲块比申请的容量大不了多少,则需要整块分配,即不保留<=e的剩余量
|
||||
if(p->size - n <= e) {
|
||||
// 由于上面已经让pav指向了下一个空闲块,所以如果pav与p相等,说明此时只有一个空闲块了(注意空闲块链表是双循环的)
|
||||
if(*pav == p) {
|
||||
*pav = NULL; // 如果仅剩一个空闲块了,那么被占用后,可利用空间表变为空表
|
||||
|
||||
// 否则,在表中删除分配的结点
|
||||
} else {
|
||||
(*pav)->llink = p->llink; // 修改pav的前驱
|
||||
p->llink->rlink = *pav; // 修改pav前驱的后继
|
||||
|
||||
/* 在上述操作中,p结点的前驱与后继并没有改变,这是为了方便将来的回收操作 */
|
||||
}
|
||||
|
||||
// 更新占用块为占用状态
|
||||
p->tag = f->tag = 1;
|
||||
|
||||
printf("日志:分配成功!申请 %d 个\"字\",实际分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, n + e, AvailableSpace(*pav));
|
||||
|
||||
// 如果空闲块很大,则从中间切割,占用后面的n个字
|
||||
} else {
|
||||
f->tag = 1; // 修改分配块的底部标志,表示其处于占用状态
|
||||
p->size -= n; // 设置剩余块大小
|
||||
|
||||
f = FootLoc(p); // 计算剩余块的新底部位置
|
||||
f->tag = 0; // 设置新底部标志为空闲
|
||||
f->uplink = p; // 新底部依然指向空闲块头部
|
||||
|
||||
p = f + 1; // 计算分配块的新头部位置
|
||||
p->tag = 1; // 设置分配块新头部标志为占用
|
||||
p->size = n; // 设置分配块的容量
|
||||
|
||||
// 修改分配块底部的链接(教材中缺失了此步骤)
|
||||
(FootLoc(p))->uplink = p;
|
||||
|
||||
printf("日志:分配成功!申请并分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, AvailableSpace(*pav));
|
||||
}
|
||||
|
||||
// 返回分配块首地址
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:以下描述中的"释放块"就是指针p指向的内存
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p) {
|
||||
Space h, f, q;
|
||||
int Ltag, Rtag;
|
||||
|
||||
if(p == NULL) {
|
||||
printf("日志:回收失败!内存指针为空。空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
return;
|
||||
}
|
||||
|
||||
Ltag = p == av ? 1 : (p - 1)->tag; // 块p的左邻区标志。特别注意,如果块p位于内存起始处,则认为它的左邻区为占用。
|
||||
Rtag = (p + p->size) == (av + len) ? 1 : (p + p->size)->tag; // 块p的右邻区标志。特别注意,如果块p位于内存结尾处,则认为它的右邻区为占用。
|
||||
|
||||
/*
|
||||
* 1.释放块的左、右邻区均为占用块
|
||||
*
|
||||
* 此时仅需要将块p插入到pav所指结点的之前即可(当然,插入到之后也是可以的)
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为占用块...\n", p->size);
|
||||
|
||||
f = FootLoc(p);
|
||||
f->uplink = p;
|
||||
f->tag = 0;
|
||||
|
||||
p->tag = 0;
|
||||
|
||||
// 空闲链表为空时,直接将块p变为新的独立的空闲块
|
||||
if((*pav) == NULL) {
|
||||
*pav = p->llink = p->rlink = p;
|
||||
|
||||
// 否则,将块p插入到pav之前
|
||||
} else {
|
||||
q = (*pav)->llink;
|
||||
p->rlink = *pav;
|
||||
p->llink = q;
|
||||
q->rlink = (*pav)->llink = p;
|
||||
|
||||
// 令刚释放的结点成为下次分配空间时最先查找的结点
|
||||
*pav = p;
|
||||
}
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2.释放块的左邻区为空闲块,右邻区为占用块
|
||||
*
|
||||
* 此时需要合并左邻区与释放块
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为空闲块,右邻区为占用块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
h->size += p->size; // 左邻区容量增大
|
||||
|
||||
f = FootLoc(p); // 将释放块的底部做为合并后的新块的底部
|
||||
f->uplink = h;
|
||||
f->tag = 0;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3.释放块的左邻区为占用块,右邻区为空闲块
|
||||
*
|
||||
* 此时需要合并释放块与右邻区
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为占用块,右邻区为空闲块...\n", p->size);
|
||||
|
||||
h = p + p->size; // 右邻区的头部
|
||||
|
||||
f = FootLoc(h); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
f->uplink = p; // 释放块的头部将作为合并后新块的头部
|
||||
|
||||
p->tag = 0;
|
||||
p->size += h->size;
|
||||
|
||||
// 释放块的头部链接域要更新为与右邻区头部的链接域一致
|
||||
p->llink = h->llink;
|
||||
p->rlink = h->rlink;
|
||||
h->llink->rlink = p;
|
||||
h->rlink->llink = p;
|
||||
|
||||
// pav指向合并后的结点的新头部
|
||||
*pav = p;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4.释放块的左、右邻区均为空闲块
|
||||
*
|
||||
* 此时需要合并左邻区、释放块、右邻区
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为空闲块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
q = p + p->size; // 右邻区的头部
|
||||
f = FootLoc(q); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
|
||||
h->size += p->size + q->size; // 合并后的新块大小
|
||||
f->uplink = h; // 新块底部信息也要更新
|
||||
|
||||
// 移除右邻区
|
||||
q->rlink->llink = q->llink;
|
||||
q->llink->rlink = q->rlink;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
Space p;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
p = av;
|
||||
count = av->size;
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("□");
|
||||
} else {
|
||||
printf("■");
|
||||
}
|
||||
|
||||
if(i == count && count < len) {
|
||||
p = p + p->size;
|
||||
count += p->size;
|
||||
printf("|");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
// 每隔20个换一下行
|
||||
if(i % 20 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(len % 20 != 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav) {
|
||||
Space p;
|
||||
int count;
|
||||
|
||||
if(pav == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = pav;
|
||||
count = 0;
|
||||
|
||||
do {
|
||||
count += p->size;
|
||||
p = p->rlink;
|
||||
} while(p != pav);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
[Project]
|
||||
FileName=BoundaryTagMethod.dev
|
||||
Name=BoundaryTagMethod
|
||||
Type=1
|
||||
Ver=2
|
||||
ObjFiles=
|
||||
Includes=
|
||||
Libs=
|
||||
PrivateResource=
|
||||
ResourceIncludes=
|
||||
MakeIncludes=
|
||||
Compiler=
|
||||
CppCompiler=
|
||||
Linker=
|
||||
IsCpp=0
|
||||
Icon=
|
||||
ExeOutput=
|
||||
ObjectOutput=
|
||||
LogOutput=
|
||||
LogOutputEnabled=0
|
||||
OverrideOutput=0
|
||||
OverrideOutputName=
|
||||
HostApplication=
|
||||
UseCustomMakefile=0
|
||||
CustomMakefile=
|
||||
CommandLine=
|
||||
Folders=
|
||||
IncludeVersionInfo=0
|
||||
SupportXPThemes=0
|
||||
CompilerSet=1
|
||||
CompilerSettings=0000000000000000001000000
|
||||
UnitCount=3
|
||||
|
||||
[VersionInfo]
|
||||
Major=1
|
||||
Minor=0
|
||||
Release=0
|
||||
Build=0
|
||||
LanguageID=1033
|
||||
CharsetID=1252
|
||||
CompanyName=
|
||||
FileVersion=
|
||||
FileDescription=Developed using the Dev-C++ IDE
|
||||
InternalName=
|
||||
LegalCopyright=
|
||||
LegalTrademarks=
|
||||
OriginalFilename=
|
||||
ProductName=
|
||||
ProductVersion=
|
||||
AutoIncBuildNr=0
|
||||
SyncProduct=1
|
||||
|
||||
[Unit2]
|
||||
FileName=BoundaryTagMethod.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit3]
|
||||
FileName=BoundaryTagMethod-main.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit1]
|
||||
FileName=BoundaryTagMethod.h
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#ifndef BOUNDARYTAGMETHOD_H
|
||||
#define BOUNDARYTAGMETHOD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define e 5 // 分配空间时用到的容差
|
||||
#define FootLoc(p) p+(p->size)-1 // 将p指针定位到p所指内存块的底部
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 教材中将llink和uplink封装在了一个联合体中,个人感觉这个封装有些鸡肋。
|
||||
* 一方面,head和foot的公共部分其实只有tag,如果真想用联合体,那么应当把size和rlink也封装起来。
|
||||
* 另一方面,这个封装节省的空间很有限,而且直接影响了代码的可读性。
|
||||
* 这里只是教学代码,而不是真实的系统代码,所以空间考虑在其次,原理展示为首要任务。
|
||||
* 此外,教材中的伪码中也并没有考虑这个联合体,而是直接进行操作的。
|
||||
* 综上所述,这里去除了教材中的联合体结构。
|
||||
* 如想观察带有联合体的写法,可以参考CFree分支的代码。
|
||||
*/
|
||||
|
||||
int tag; // 块标志,0空闲,1占用,头部和尾部均有
|
||||
|
||||
struct WORD* llink; // 头部域,指向前驱结点
|
||||
struct WORD* rlink; // 头部域,指向后继结点
|
||||
int size; // 头部域,块大小
|
||||
|
||||
struct WORD* uplink; // 底部域,指向本结点头部
|
||||
} WORD;
|
||||
|
||||
typedef WORD* Space; // Space:指向可利用空间的指针类型
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n);
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav);
|
||||
|
||||
#endif
|
||||
|
||||
45
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem-main.cpp
Normal file
45
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem-main.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdio.h>
|
||||
#include "BuddySystem.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
FreeList avail;
|
||||
WORD* p[8]; // 记录申请到的内存的指针
|
||||
int s[8] = {4, 5, 6, 7, 1, 5, 3, 9}; // 申请的空间大小
|
||||
int i;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
printf("初始化一个内存块...\n");
|
||||
InitSpace(avail);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBuddy \n");
|
||||
{
|
||||
for(i = 0; i < 8; i++) {
|
||||
printf("申请大小为 %d 个字的内存块...\n", s[i]);
|
||||
p[i] = AllocBuddy(avail, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBuddy \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[8] = {2, 0, 5, 7, 1, 4, 3, 6};
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
FreeBuddy(avail, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
284
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.cpp
Normal file
284
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#include "BuddySystem.h"
|
||||
|
||||
// 记录内存的起始地址,在计算伙伴块时候需要用到
|
||||
WORD* start;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail) {
|
||||
int k;
|
||||
WORD* r;
|
||||
|
||||
// 遍历M+1个元素
|
||||
for(k = 0; k <= M; k++) {
|
||||
avail[k].nodesize = (int) pow(2, k);
|
||||
avail[k].first = NULL;
|
||||
}
|
||||
|
||||
r = (WORD*) malloc((int) pow(2, M) * sizeof(WORD));
|
||||
if(r == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 设置头部信息
|
||||
r->llink = r->rlink = r;
|
||||
r->tag = 0;
|
||||
r->kval = M;
|
||||
|
||||
avail[M].first = r;
|
||||
|
||||
start = r;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n) {
|
||||
int k, i;
|
||||
WORD* pa, * pre, * suc, * pi;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于1,则直接返回。
|
||||
*/
|
||||
if(n < 1) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于1\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(k = 0; k <= M && (avail[k].nodesize < n + 1 || !avail[k].first); k++) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(k > M) {
|
||||
printf("日志:分配失败!没有足够的空闲块\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa = avail[k].first; // 指向可分配子表的第一个结点
|
||||
pre = pa->llink; // 分别记下前驱和后继
|
||||
suc = pa->rlink;
|
||||
|
||||
// 如果此处仅有一个空闲块,则分配后该子表变为空
|
||||
if(pa == suc) {
|
||||
avail[k].first = NULL;
|
||||
|
||||
// 否则,从链表头部摘下一个可用的空闲块,并将子表头指针指向下一个空闲块
|
||||
} else {
|
||||
pre->rlink = suc;
|
||||
suc->llink = pre;
|
||||
avail[k].first = suc;
|
||||
}
|
||||
|
||||
/*
|
||||
* 从k-1处开始逆向遍历FreeList数组,向其中填充剩余的空闲块。
|
||||
* 剩余的空闲块是对剩余可用空间的拆分。
|
||||
*
|
||||
* 这里用到一个公式:2^m-2^n = 2^n+2^(n+1)+...+2^(m-1)
|
||||
* 比如初始容量为2^16,此时总共申请1500个字,那么需要分配一块2^11的空闲块给它。
|
||||
* 分配完之后,剩余的容量为:2^16-2^11 = 2^11+2^12+2^13+2^14+2^15。
|
||||
* 这些剩余容量可以拆分为5个空闲块,分别存储到15、14、13、12、11这五个索引处。
|
||||
*/
|
||||
for(i = 1; k - i >= 0 && avail[k - i].nodesize >= n + 1; i++) {
|
||||
pi = pa + (int) pow(2, k - i); // 每次将pi指向剩余空间的后一半
|
||||
pi->rlink = pi->llink = pi; // 初始化pi的前驱和后继
|
||||
pi->tag = 0; // 标记为空闲块
|
||||
pi->kval = k - i; // 设置该块的容量标志,真实容量为2^(k-i)
|
||||
avail[k - i].first = pi;
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 上面分解出来的pi直接添加到了avail中,并没有考虑同位置处会不会有别的容量相同的空闲块。
|
||||
* 这里不需要考虑的原因是如果同位置处已经存在别的容量相同的空闲块,
|
||||
* 那么这里根本不需要分解剩余空间,换句话说,连这个循环都进不来。
|
||||
* 只要进来这个循环,说明目标位置处已经为空了,没有找到合适的空闲块,所以这才进一步向高游标处寻找空闲块。
|
||||
*/
|
||||
}
|
||||
|
||||
// 最后剩下的最靠前的空间就是需要分配的空间(这里没有设置pa的前驱和后继,因为没必要)
|
||||
pa->tag = 1;
|
||||
pa->kval = k - (--i);
|
||||
|
||||
printf("日志:分配成功!用户申请 %d 个字,系统申请 %d 个字,实际分配 %d 个字\n", n, n + 1, (int) pow(2, pa->kval));
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p) {
|
||||
int k;
|
||||
WORD* r;
|
||||
WORD* buddy = Buddy(p);
|
||||
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 这里将p结点简单地插入到avail中,包含三种情形:
|
||||
* 1.伙伴块非空闲
|
||||
* 2.伙伴空闲,但是伙伴的大小跟p的大小不一致,说明伙伴还没拼合好
|
||||
* 3.p拼接成了最后一个最大的空闲块
|
||||
*/
|
||||
if(buddy->tag == 1 || buddy->kval != p->kval || p->kval == M) {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找p结点应当进入的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,采用头插法将空闲块插入到目标插槽
|
||||
if(k <= M && k == p->kval) {
|
||||
p->tag = 0;
|
||||
|
||||
if(avail[k].first == NULL) {
|
||||
p->llink = p->rlink = p;
|
||||
} else {
|
||||
p->llink = avail[k].first->llink;
|
||||
p->rlink = avail[k].first;
|
||||
p->llink->rlink = p;
|
||||
p->rlink->llink = p;
|
||||
}
|
||||
|
||||
avail[k].first = p;
|
||||
|
||||
printf("日志:回收成功![%d, (2^%d)]进入插槽 %d 的空闲块链表上\n", (int) (p - start), k, k);
|
||||
}
|
||||
|
||||
// 如果伙伴块是空闲的,此时应当进行合并操作
|
||||
} else {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找伙伴块所在的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,将伙伴块从空闲块链表中摘下来
|
||||
if(k <= M && k == p->kval) {
|
||||
// 伙伴在链表第一个位置
|
||||
if(avail[k].first == buddy) {
|
||||
buddy->rlink->llink = buddy->llink;
|
||||
buddy->llink->rlink = buddy->rlink;
|
||||
|
||||
avail[k].first = buddy->rlink;
|
||||
|
||||
// 伙伴在中间位置
|
||||
} else {
|
||||
for(r = avail[k].first; r->rlink != buddy; r = r->rlink) {
|
||||
// 查找伙伴,r指向伙伴的前驱
|
||||
}
|
||||
|
||||
r->rlink = buddy->rlink;
|
||||
buddy->rlink->llink = r;
|
||||
}
|
||||
|
||||
printf("日志:合并成功![%d, (2^%d)]和[%d, (2^%d)]合并成了", (int) (p - start), k, (int) (buddy - start), k);
|
||||
|
||||
// 合并之前,需要确定哪个伙伴靠前
|
||||
if(p < buddy) {
|
||||
p->tag = 0;
|
||||
} else {
|
||||
p = buddy;
|
||||
}
|
||||
|
||||
p->kval = k + 1; // 指数增一后,即完成合并
|
||||
|
||||
printf("[%d, (2^%d)]\n", (int) (p - start), k + 1);
|
||||
|
||||
// 出现新的空闲块之后,要进入递归,查看该空闲块是否也存在空闲伙伴
|
||||
FreeBuddy(avail, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
int i, count, total;
|
||||
WORD* p;
|
||||
|
||||
printf("|");
|
||||
|
||||
p = start;
|
||||
count = (int) pow(2, p->kval);
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("_");
|
||||
} else {
|
||||
printf("*");
|
||||
}
|
||||
|
||||
// 进入到下一个块
|
||||
if(i == count && count < (int) pow(2, M)) {
|
||||
p = start + count;
|
||||
count += (int) pow(2, p->kval);
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p) {
|
||||
long s, m, n;
|
||||
|
||||
if(p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start是整个空闲块的绝对起始地址,s是p在伙伴系统中的绝对地址,从0开始
|
||||
s = p - start;
|
||||
if(s < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m = (long) pow(2, p->kval);
|
||||
n = (long) pow(2, p->kval + 1);
|
||||
|
||||
if(s % n == 0) {
|
||||
return p + m;
|
||||
}
|
||||
|
||||
if(s % n == m) {
|
||||
return p - m;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
82
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.dev
Normal file
82
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.dev
Normal file
@@ -0,0 +1,82 @@
|
||||
[Project]
|
||||
FileName=BuddySystem.dev
|
||||
Name=BuddySystem
|
||||
Type=1
|
||||
Ver=2
|
||||
ObjFiles=
|
||||
Includes=
|
||||
Libs=
|
||||
PrivateResource=
|
||||
ResourceIncludes=
|
||||
MakeIncludes=
|
||||
Compiler=
|
||||
CppCompiler=
|
||||
Linker=
|
||||
IsCpp=0
|
||||
Icon=
|
||||
ExeOutput=
|
||||
ObjectOutput=
|
||||
LogOutput=
|
||||
LogOutputEnabled=0
|
||||
OverrideOutput=0
|
||||
OverrideOutputName=
|
||||
HostApplication=
|
||||
UseCustomMakefile=0
|
||||
CustomMakefile=
|
||||
CommandLine=
|
||||
Folders=
|
||||
IncludeVersionInfo=0
|
||||
SupportXPThemes=0
|
||||
CompilerSet=1
|
||||
CompilerSettings=0000000000000000001000000
|
||||
UnitCount=3
|
||||
|
||||
[VersionInfo]
|
||||
Major=1
|
||||
Minor=0
|
||||
Release=0
|
||||
Build=0
|
||||
LanguageID=1033
|
||||
CharsetID=1252
|
||||
CompanyName=
|
||||
FileVersion=
|
||||
FileDescription=Developed using the Dev-C++ IDE
|
||||
InternalName=
|
||||
LegalCopyright=
|
||||
LegalTrademarks=
|
||||
OriginalFilename=
|
||||
ProductName=
|
||||
ProductVersion=
|
||||
AutoIncBuildNr=0
|
||||
SyncProduct=1
|
||||
|
||||
[Unit1]
|
||||
FileName=BuddySystem.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit2]
|
||||
FileName=BuddySystem.h
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit3]
|
||||
FileName=BuddySystem-main.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
90
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
90
Dev-C++/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#ifndef BUDDYSYSTEM_H
|
||||
#define BUDDYSYSTEM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define M 6 // 假设总空间大小为2^M个字,子表个数为M+1
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
struct WORD* llink; // 头部域的前驱指针
|
||||
struct WORD* rlink; // 头部域的后继指针
|
||||
int tag; // 头部域的块标志,0:空闲,1:占用
|
||||
int kval; // 指示块的大小,比如其值为K时,表示该块的大小为2^K
|
||||
} WORD;
|
||||
|
||||
// 表头向量类型
|
||||
typedef struct HeadNode {
|
||||
int nodesize; // 该链表的空闲块的大小
|
||||
WORD* first; // 该链表的表头指针
|
||||
} FreeList[M + 1];
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n);
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p);
|
||||
|
||||
#endif
|
||||
|
||||
202
Dev-C++/CourseBook/0803_GarbageCollection/GList-HT.cpp
Normal file
202
Dev-C++/CourseBook/0803_GarbageCollection/GList-HT.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#include "GList-HT.h" //**▲05 数组和广义表**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L) {
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*L = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S) {
|
||||
SString emp; // 代表空广义表的字符串
|
||||
SString hsub, sub;
|
||||
GList p, q;
|
||||
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 清理字符串S中的空白,包括清理不可打印字符和清理空格
|
||||
ClearBlank(S);
|
||||
|
||||
if(StrEmpty(S)) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
StrAssign(emp, "()");
|
||||
|
||||
/*
|
||||
* 如果输入串为(),则代表需要创建空的广义表
|
||||
*
|
||||
*【注】
|
||||
* 教材这里的代码是有问题的。
|
||||
* StrCompare的返回值指示的是两个字符串的大小,而不是指示两个字符串是否相等。
|
||||
* 如果给定的S与()相等,返回值应当是0。
|
||||
*/
|
||||
if(!StrCompare(S, emp)) {
|
||||
*L = NULL;
|
||||
} else {
|
||||
*L = (GList) malloc(sizeof(GLNode));
|
||||
if(*L == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
(*L)->mark = 0;
|
||||
|
||||
// 创建原子
|
||||
if(StrLength(S) == 1) {
|
||||
(*L)->tag = Atom;
|
||||
(*L)->Node.atom = S[1];
|
||||
} else {
|
||||
(*L)->tag = List;
|
||||
|
||||
p = *L;
|
||||
|
||||
// 去掉最外层括号
|
||||
SubString(sub, S, 2, StrLength(S) - 2);
|
||||
|
||||
// 重复建n个子表
|
||||
do {
|
||||
// 从sub中分离出表头串hsub,分离完成后,sub也会发生变化
|
||||
sever(hsub, sub);
|
||||
|
||||
// 递归创建广义表
|
||||
CreateGList(&(p->Node.ptr.hp), hsub);
|
||||
|
||||
q = p;
|
||||
|
||||
// 如果表尾不为空,需要继续处理表尾
|
||||
if(!StrEmpty(sub)) {
|
||||
p = (GList) malloc(sizeof(GLNode));
|
||||
if(p == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
p->mark = 0;
|
||||
|
||||
p->tag = List;
|
||||
|
||||
q->Node.ptr.tp = p;
|
||||
}
|
||||
} while(!StrEmpty(sub));
|
||||
|
||||
q->Node.ptr.tp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L) {
|
||||
Print(L, Head);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark) {
|
||||
// L为空
|
||||
if(L == NULL) {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
}
|
||||
|
||||
printf(")");
|
||||
|
||||
// L不为空时
|
||||
} else {
|
||||
// 对于原子结点,输出原子
|
||||
if(L->tag == Atom) {
|
||||
printf("%c", L->Node.atom);
|
||||
|
||||
// 对于子表结点,要对表头、表尾分别讨论
|
||||
} else {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
} else {
|
||||
printf(",");
|
||||
}
|
||||
|
||||
Print(L->Node.ptr.hp, Head);
|
||||
Print(L->Node.ptr.tp, Tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str) {
|
||||
int i, k, n;
|
||||
SString ch;
|
||||
|
||||
n = StrLength(str);
|
||||
|
||||
i = 0; // 遍历字符串时的游标
|
||||
k = 0; // 标记遇到的未配对括号数量
|
||||
|
||||
do {
|
||||
++i;
|
||||
|
||||
// 截取str第一个字符
|
||||
SubString(ch, str, i, 1);
|
||||
|
||||
if(ch[1] == '(') {
|
||||
++k;
|
||||
}
|
||||
|
||||
if(ch[1] == ')') {
|
||||
--k;
|
||||
}
|
||||
} while(i < n && (ch[1] != ',' || k != 0));
|
||||
|
||||
// 如果存在多个广义表结点
|
||||
if(i < n) {
|
||||
SubString(hstr, str, 1, i - 1);
|
||||
SubString(str, str, i + 1, n - i);
|
||||
|
||||
// 只有一个广义表结点
|
||||
} else {
|
||||
StrCopy(hstr, str);
|
||||
ClearString(str);
|
||||
}
|
||||
}
|
||||
|
||||
97
Dev-C++/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
97
Dev-C++/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#ifndef GLIST_HT_H
|
||||
#define GLIST_HT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供 malloc、realloc、free、exit 原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "SString.h" //**▲04 串**//
|
||||
|
||||
/* 原子元素类型 */
|
||||
typedef char AtomType;
|
||||
|
||||
/*
|
||||
* 广义表结点标记
|
||||
*
|
||||
* Atom-0:原子结点
|
||||
* List-1:表结点
|
||||
*/
|
||||
typedef enum { Atom, List } ElemTag;
|
||||
|
||||
/* 广义表(头尾链表存储表示)类型定义 */
|
||||
typedef struct GLNode {
|
||||
int mark; // 为第8章第5节新增的变量,用来给广义表打上遍历标记,会在创建中初始化为0
|
||||
|
||||
ElemTag tag; // 公共标记,用于区分原子结点和表结点
|
||||
|
||||
// 原子结点和表结点的联合部分
|
||||
union {
|
||||
AtomType atom; // atom是原子结点的值域,AtomType由用户定义
|
||||
struct {
|
||||
struct GLNode* hp; // 指向表头
|
||||
struct GLNode* tp; // 指向表尾
|
||||
} ptr; // 表结点的指针域
|
||||
} Node;
|
||||
} GLNode;
|
||||
|
||||
/* 广义表类型 */
|
||||
typedef GLNode* GList;
|
||||
|
||||
/*
|
||||
* 图形化输出标记
|
||||
*
|
||||
* Head代表广义表指针来自表头
|
||||
* Tail代表广义表指针来自表尾
|
||||
*/
|
||||
typedef enum { Head, Tail } Mark;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S);
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L);
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <stdio.h>
|
||||
#include "GarbageCollection.h" //**▲08 动态存储管理**//
|
||||
|
||||
// 遍历广义表
|
||||
void Traverse(GList L, void(Visit)(GList));
|
||||
|
||||
// 打印广义表结点信息
|
||||
void PrintInfo(GList L);
|
||||
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
GList G;
|
||||
|
||||
printf("创建并输出广义表:");
|
||||
{
|
||||
SString S;
|
||||
char* s = "((),(e),(a,(b,c,d)))";
|
||||
|
||||
InitGList(&G);
|
||||
|
||||
StrAssign(S, s);
|
||||
CreateGList(&G, S);
|
||||
|
||||
PrintGList(G);
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ MakeList \n");
|
||||
{
|
||||
printf("访问前的标志状态...\n");
|
||||
Traverse(G, PrintInfo);
|
||||
printf("\n");
|
||||
MakeList(G);
|
||||
|
||||
printf("访问后的标志状态...\n");
|
||||
Traverse(G, PrintInfo);
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// 遍历广义表
|
||||
void Traverse(GList L, void(Visit)(GList)) {
|
||||
if(L == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Visit(L);
|
||||
|
||||
if(L->tag == List) {
|
||||
Traverse(L->Node.ptr.hp, Visit);
|
||||
Traverse(L->Node.ptr.tp, Visit);
|
||||
}
|
||||
}
|
||||
|
||||
// 打印广义表结点信息
|
||||
void PrintInfo(GList L) {
|
||||
if(L->tag == Atom) {
|
||||
printf("mark = %d 原子结点:%c\n", L->mark, L->Node.atom);
|
||||
} else {
|
||||
printf("mark = %d 表结点\n", L->mark);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*==============
|
||||
* 无用单元收集
|
||||
*
|
||||
* 包含算法: 8.3
|
||||
===============*/
|
||||
|
||||
#include "GarbageCollection.h"
|
||||
|
||||
/*
|
||||
* ████████ 算法8.3 ████████
|
||||
*
|
||||
* 遍历广义表(不使用栈)。
|
||||
*/
|
||||
void MakeList(GList G) {
|
||||
GList t, p, q;
|
||||
Status finished;
|
||||
|
||||
if(!G || G->mark != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
t = NULL; // t为p的母表
|
||||
p = G;
|
||||
finished = FALSE;
|
||||
|
||||
while(!finished) {
|
||||
while(p->mark == 0) {
|
||||
p->mark = 1; // MakeHead(p)的细化
|
||||
q = p->Node.ptr.hp; // q指向p的表头
|
||||
|
||||
if(q != NULL && q->mark == 0) {
|
||||
// 表头为原子结点
|
||||
if(q->tag == Atom) {
|
||||
q->mark = 1;
|
||||
|
||||
// 继续遍历子表
|
||||
} else {
|
||||
p->Node.ptr.hp = t;
|
||||
p->tag = Atom;
|
||||
t = p;
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
} // 完成对表头的标记
|
||||
|
||||
q = p->Node.ptr.tp;
|
||||
|
||||
// 继续遍历表尾
|
||||
if(q != NULL && q->mark == 0) {
|
||||
p->Node.ptr.tp = t;
|
||||
t = p;
|
||||
p = q;
|
||||
|
||||
// BackTrack(finished)的细化
|
||||
} else {
|
||||
// 表结点,从表尾回溯
|
||||
while(t && t->tag == List) {
|
||||
q = t;
|
||||
t = q->Node.ptr.tp;
|
||||
q->Node.ptr.tp = p;
|
||||
p = q;
|
||||
}
|
||||
|
||||
// 结束
|
||||
if(t == NULL) {
|
||||
finished = TRUE;
|
||||
|
||||
// 从表头回溯
|
||||
} else {
|
||||
q = t;
|
||||
t = q->Node.ptr.hp;
|
||||
q->Node.ptr.hp = p;
|
||||
p = q;
|
||||
p->tag = List;
|
||||
}// 继续遍历表尾
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
122
Dev-C++/CourseBook/0803_GarbageCollection/GarbageCollection.dev
Normal file
122
Dev-C++/CourseBook/0803_GarbageCollection/GarbageCollection.dev
Normal file
@@ -0,0 +1,122 @@
|
||||
[Project]
|
||||
FileName=GarbageCollection.dev
|
||||
Name=GarbageCollection
|
||||
Type=1
|
||||
Ver=2
|
||||
ObjFiles=
|
||||
Includes=
|
||||
Libs=
|
||||
PrivateResource=
|
||||
ResourceIncludes=
|
||||
MakeIncludes=
|
||||
Compiler=
|
||||
CppCompiler=
|
||||
Linker=
|
||||
IsCpp=0
|
||||
Icon=
|
||||
ExeOutput=
|
||||
ObjectOutput=
|
||||
LogOutput=
|
||||
LogOutputEnabled=0
|
||||
OverrideOutput=0
|
||||
OverrideOutputName=
|
||||
HostApplication=
|
||||
UseCustomMakefile=0
|
||||
CustomMakefile=
|
||||
CommandLine=
|
||||
Folders=
|
||||
IncludeVersionInfo=0
|
||||
SupportXPThemes=0
|
||||
CompilerSet=1
|
||||
CompilerSettings=0000000000000000001000000
|
||||
UnitCount=7
|
||||
|
||||
[VersionInfo]
|
||||
Major=1
|
||||
Minor=0
|
||||
Release=0
|
||||
Build=0
|
||||
LanguageID=1033
|
||||
CharsetID=1252
|
||||
CompanyName=
|
||||
FileVersion=
|
||||
FileDescription=Developed using the Dev-C++ IDE
|
||||
InternalName=
|
||||
LegalCopyright=
|
||||
LegalTrademarks=
|
||||
OriginalFilename=
|
||||
ProductName=
|
||||
ProductVersion=
|
||||
AutoIncBuildNr=0
|
||||
SyncProduct=1
|
||||
|
||||
[Unit1]
|
||||
FileName=GarbageCollection.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit2]
|
||||
FileName=GarbageCollection.h
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit3]
|
||||
FileName=GarbageCollection-main.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit4]
|
||||
FileName=GList-HT.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit5]
|
||||
FileName=GList-HT.h
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit6]
|
||||
FileName=SString.cpp
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
[Unit7]
|
||||
FileName=SString.h
|
||||
CompileCpp=0
|
||||
Folder=
|
||||
Compile=1
|
||||
Link=1
|
||||
Priority=1000
|
||||
OverrideBuildCmd=0
|
||||
BuildCmd=
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*==============
|
||||
* 无用单元收集
|
||||
*
|
||||
* 包含算法: 8.3
|
||||
===============*/
|
||||
|
||||
#ifndef GARBAGECOLLECTION_H
|
||||
#define GARBAGECOLLECTION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "GList-HT.h" //**▲05 数组和广义表**//
|
||||
|
||||
/*
|
||||
* ████████ 算法8.3 ████████
|
||||
*
|
||||
* 遍历广义表(不使用栈)。
|
||||
*/
|
||||
void MakeList(GList G);
|
||||
|
||||
#endif
|
||||
|
||||
168
Dev-C++/CourseBook/0803_GarbageCollection/SString.cpp
Normal file
168
Dev-C++/CourseBook/0803_GarbageCollection/SString.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*=============================
|
||||
* 串的定长顺序存储表示(顺序串)
|
||||
*
|
||||
* 包含算法: 4.1、4.2、4.3、4.5
|
||||
==============================*/
|
||||
|
||||
#include "SString.h" //**▲04 串**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个值为chars的串T。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status StrAssign(SString T, const char* chars) {
|
||||
int i, len;
|
||||
|
||||
len = (int) strlen(chars);
|
||||
|
||||
// chars过长
|
||||
if(len > MAXSTRLEN) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
T[0] = len;
|
||||
for(i = 1; i <= len; i++) {
|
||||
T[i] = chars[i - 1];
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 清空
|
||||
*
|
||||
* 将串S清空。
|
||||
*/
|
||||
Status ClearString(SString S) {
|
||||
// 只需要将长度置为0就可以
|
||||
S[0] = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断串S中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 串S为空
|
||||
* FALSE: 串S不为空
|
||||
*/
|
||||
Status StrEmpty(SString S) {
|
||||
return S[0] == 0 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回串S中元素的个数。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrLength(SString S) {
|
||||
return S[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法4.3 ████████
|
||||
*
|
||||
* 求子串
|
||||
*
|
||||
* 用Sub返回S[pos, pos+len-1]。
|
||||
* 返回值指示是否截取成功。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status SubString(SString Sub, SString S, int pos, int len) {
|
||||
int i;
|
||||
|
||||
if(pos < 1 || pos > S[0] || len < 0 || pos + len - 1 > S[0]) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 复制元素
|
||||
for(i = 1; i <= len; i++) {
|
||||
Sub[i] = S[pos + i - 1];
|
||||
}
|
||||
|
||||
// 确定新长度
|
||||
Sub[0] = len;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 比较
|
||||
*
|
||||
* 比较串S和串T,返回比较结果。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrCompare(SString S, SString T) {
|
||||
int i = 1;
|
||||
|
||||
while(i <= S[0] && i <= T[0]) {
|
||||
// 遇到不同的字符时,比较其大小
|
||||
if(S[i] != T[i]) {
|
||||
return S[i] - T[i];
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return S[0] - T[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* 复制
|
||||
*
|
||||
* 将串S复制到串T。
|
||||
*/
|
||||
Status StrCopy(SString T, SString S) {
|
||||
int i;
|
||||
|
||||
// 连同长度信息一起复制
|
||||
for(i = 0; i <= S[0]; i++) {
|
||||
T[i] = S[i];
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 清理字符串S中的空白,包括清理不可打印字符和清理空格。
|
||||
*
|
||||
*【注】
|
||||
* 该函数是在本章中新增的。
|
||||
*/
|
||||
Status ClearBlank(SString S) {
|
||||
int i, j;
|
||||
|
||||
// 如果是空串
|
||||
if(S[0] == 0) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
for(i = 1, j = 0; i <= S[0]; i++) {
|
||||
// 如果遇到空白,则略过
|
||||
if(S[i] == ' ' || !isprint(S[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
j++;
|
||||
|
||||
S[j] = S[i];
|
||||
}
|
||||
|
||||
S[0] = j;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
111
Dev-C++/CourseBook/0803_GarbageCollection/SString.h
Normal file
111
Dev-C++/CourseBook/0803_GarbageCollection/SString.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*=============================
|
||||
* 串的定长顺序存储表示(顺序串)
|
||||
*
|
||||
* 包含算法: 4.1、4.2、4.3、4.5
|
||||
==============================*/
|
||||
|
||||
#ifndef SSTRING_H
|
||||
#define SSTRING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h> // 提供 strlen 原型
|
||||
#include <ctype.h> // 提供 isprint 原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define MAXSTRLEN 255 // 顺序串的最大串长
|
||||
|
||||
/*
|
||||
* 串的顺序存储类型定义
|
||||
*
|
||||
* 注:有效元素从SString的1号单元开始存储
|
||||
* SString的0号单元用来存储其长度
|
||||
*/
|
||||
typedef unsigned char SString[MAXSTRLEN + 1]; // 0号单元存放串的长度
|
||||
|
||||
|
||||
/*
|
||||
* ████ 提示 ████
|
||||
*
|
||||
* 遵循教材的书写习惯,pos指示字符的位序(不是索引),从1开始计数
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 构造一个值为chars的串T。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status StrAssign(SString T, const char* chars);
|
||||
|
||||
/*
|
||||
* 清空
|
||||
*
|
||||
* 将串S清空。
|
||||
*/
|
||||
Status ClearString(SString S);
|
||||
|
||||
/*
|
||||
* 判空
|
||||
*
|
||||
* 判断串S中是否包含有效数据。
|
||||
*
|
||||
* 返回值:
|
||||
* TRUE : 串S为空
|
||||
* FALSE: 串S不为空
|
||||
*/
|
||||
Status StrEmpty(SString S);
|
||||
|
||||
/*
|
||||
* 计数
|
||||
*
|
||||
* 返回串S中元素的个数。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrLength(SString S);
|
||||
|
||||
/*
|
||||
* ████████ 算法4.3 ████████
|
||||
*
|
||||
* 求子串
|
||||
*
|
||||
* 用Sub返回S[pos, pos+len-1]。
|
||||
* 返回值指示是否截取成功。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
Status SubString(SString Sub, SString S, int pos, int len);
|
||||
|
||||
/*
|
||||
* 比较
|
||||
*
|
||||
* 比较串S和串T,返回比较结果。
|
||||
*
|
||||
*【注】
|
||||
* 该操作属于最小操作子集
|
||||
*/
|
||||
int StrCompare(SString S, SString T);
|
||||
|
||||
/*
|
||||
* 复制
|
||||
*
|
||||
* 将串S复制到串T。
|
||||
*/
|
||||
Status StrCopy(SString T, SString S);
|
||||
|
||||
/*
|
||||
* 清理字符串S中的空白,包括清理不可打印字符和清理空格。
|
||||
*
|
||||
*【注】
|
||||
* 该函数是在本章中新增的。
|
||||
*/
|
||||
Status ClearBlank(SString S);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D71FCDD5-A659-4ABD-9BBD-D534E0500789}</ProjectGuid>
|
||||
<RootNamespace>My0801_BoundaryTagMethod</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(SolutionDir)\..\Status;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(SolutionDir)\..\Status\Status.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BoundaryTagMethod-main.c" />
|
||||
<ClCompile Include="BoundaryTagMethod.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BoundaryTagMethod.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BoundaryTagMethod.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BoundaryTagMethod-main.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BoundaryTagMethod.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Space pav;
|
||||
Space p[12]; // 记录申请到的内存的指针
|
||||
int s[12] = {10, 20, 30, 50, 5, 15, 10, 5, 15, 15, 2, 20}; // 申请的空间大小
|
||||
int i = 0;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
int max = 200; // 初值建议为20的倍数,目的是打印出来可以对齐
|
||||
|
||||
printf("初始化包含 %d 个\"字\"的内存块后,当前内存布局为:\n", max);
|
||||
pav = InitSpace(max);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBoundTag \n");
|
||||
{
|
||||
for(i = 0; i < 12; i++) {
|
||||
printf("████ %2d> 申请 %d 个\"字\"的内存后,当前内存布局为:\n", i + 1, s[i]);
|
||||
p[i] = AllocBoundTag(&pav, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBoundTag \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[10] = {7, 3, 10, 6, 8, 5, 11, 1, 0, 4};
|
||||
|
||||
for(i = 0; i < 10; i++) {
|
||||
printf("回收 p%d 指向的内存...\n", a[i] + 1);
|
||||
FreeBoundTag(&pav, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
330
VisualC++/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.c
Normal file
330
VisualC++/CourseBook/0801_BoundaryTagMethod/BoundaryTagMethod.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#include "BoundaryTagMethod.h" //**▲08 动态存储管理**//
|
||||
|
||||
/*
|
||||
* 全局变量:永远指向初始空间的头部。
|
||||
* 该变量有两个作用:
|
||||
* 1.用来追踪内存布局,即查看内存的使用情况。
|
||||
* 2.指示内存的起始和结尾
|
||||
*/
|
||||
static Space av;
|
||||
|
||||
// 记录空间容量,用于追踪内存布局
|
||||
static int len;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块总大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n) {
|
||||
Space space, head, foot;
|
||||
|
||||
// 初始化空闲内存
|
||||
space = (Space) malloc(n * sizeof(WORD));
|
||||
if(space == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 初始化头部信息
|
||||
head = space;
|
||||
head->llink = space; // 前驱指向自身
|
||||
head->rlink = space; // 后继指向自身
|
||||
head->size = n; // 空间大小,已经包含了head和foot
|
||||
head->tag = 0; // 标记空间空闲
|
||||
|
||||
// 初始化底部信息
|
||||
foot = FootLoc(head); // 初始化底部指针
|
||||
foot->uplink = head; // 底部域链接到本结点的头部
|
||||
foot->tag = 0; // 标记空间空闲
|
||||
|
||||
// 记下空间的起始位置和容量
|
||||
av = space;
|
||||
len = n;
|
||||
|
||||
return space;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n) {
|
||||
Space p, f;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于3,则直接返回。
|
||||
* 因为head和foot本身就占了2个字空间,用户至少申请1个字的话,总申请空间至少为3个字。
|
||||
*/
|
||||
if(n < 3) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于3\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(p = *pav; p && p->size < n && p->rlink != *pav; p = p->rlink) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(!p || p->size < n) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 至此,p指向了满足条件的空闲块 */
|
||||
|
||||
// 让f指向该空闲块的底部
|
||||
f = FootLoc(p);
|
||||
|
||||
// pav指向p结点的后继结点,即指向下一个空闲块
|
||||
*pav = p->rlink;
|
||||
|
||||
// 如果空闲块比申请的容量大不了多少,则需要整块分配,即不保留<=e的剩余量
|
||||
if(p->size - n <= e) {
|
||||
// 由于上面已经让pav指向了下一个空闲块,所以如果pav与p相等,说明此时只有一个空闲块了(注意空闲块链表是双循环的)
|
||||
if(*pav == p) {
|
||||
*pav = NULL; // 如果仅剩一个空闲块了,那么被占用后,可利用空间表变为空表
|
||||
|
||||
// 否则,在表中删除分配的结点
|
||||
} else {
|
||||
(*pav)->llink = p->llink; // 修改pav的前驱
|
||||
p->llink->rlink = *pav; // 修改pav前驱的后继
|
||||
|
||||
/* 在上述操作中,p结点的前驱与后继并没有改变,这是为了方便将来的回收操作 */
|
||||
}
|
||||
|
||||
// 更新占用块为占用状态
|
||||
p->tag = f->tag = 1;
|
||||
|
||||
printf("日志:分配成功!申请 %d 个\"字\",实际分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, n + e, AvailableSpace(*pav));
|
||||
|
||||
// 如果空闲块很大,则从中间切割,占用后面的n个字
|
||||
} else {
|
||||
f->tag = 1; // 修改分配块的底部标志,表示其处于占用状态
|
||||
p->size -= n; // 设置剩余块大小
|
||||
|
||||
f = FootLoc(p); // 计算剩余块的新底部位置
|
||||
f->tag = 0; // 设置新底部标志为空闲
|
||||
f->uplink = p; // 新底部依然指向空闲块头部
|
||||
|
||||
p = f + 1; // 计算分配块的新头部位置
|
||||
p->tag = 1; // 设置分配块新头部标志为占用
|
||||
p->size = n; // 设置分配块的容量
|
||||
|
||||
// 修改分配块底部的链接(教材中缺失了此步骤)
|
||||
(FootLoc(p))->uplink = p;
|
||||
|
||||
printf("日志:分配成功!申请并分配了 %d 个\"字\"。空闲空间容量为 %d ...\n", n, AvailableSpace(*pav));
|
||||
}
|
||||
|
||||
// 返回分配块首地址
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:以下描述中的"释放块"就是指针p指向的内存
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p) {
|
||||
Space h, f, q;
|
||||
int Ltag, Rtag;
|
||||
|
||||
if(p == NULL) {
|
||||
printf("日志:回收失败!内存指针为空。空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
return;
|
||||
}
|
||||
|
||||
Ltag = p == av ? 1 : (p - 1)->tag; // 块p的左邻区标志。特别注意,如果块p位于内存起始处,则认为它的左邻区为占用。
|
||||
Rtag = (p + p->size) == (av + len) ? 1 : (p + p->size)->tag; // 块p的右邻区标志。特别注意,如果块p位于内存结尾处,则认为它的右邻区为占用。
|
||||
|
||||
/*
|
||||
* 1.释放块的左、右邻区均为占用块
|
||||
*
|
||||
* 此时仅需要将块p插入到pav所指结点的之前即可(当然,插入到之后也是可以的)
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为占用块...\n", p->size);
|
||||
|
||||
f = FootLoc(p);
|
||||
f->uplink = p;
|
||||
f->tag = 0;
|
||||
|
||||
p->tag = 0;
|
||||
|
||||
// 空闲链表为空时,直接将块p变为新的独立的空闲块
|
||||
if((*pav) == NULL) {
|
||||
*pav = p->llink = p->rlink = p;
|
||||
|
||||
// 否则,将块p插入到pav之前
|
||||
} else {
|
||||
q = (*pav)->llink;
|
||||
p->rlink = *pav;
|
||||
p->llink = q;
|
||||
q->rlink = (*pav)->llink = p;
|
||||
|
||||
// 令刚释放的结点成为下次分配空间时最先查找的结点
|
||||
*pav = p;
|
||||
}
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2.释放块的左邻区为空闲块,右邻区为占用块
|
||||
*
|
||||
* 此时需要合并左邻区与释放块
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 1) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为空闲块,右邻区为占用块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
h->size += p->size; // 左邻区容量增大
|
||||
|
||||
f = FootLoc(p); // 将释放块的底部做为合并后的新块的底部
|
||||
f->uplink = h;
|
||||
f->tag = 0;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3.释放块的左邻区为占用块,右邻区为空闲块
|
||||
*
|
||||
* 此时需要合并释放块与右邻区
|
||||
*/
|
||||
if(Ltag == 1 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左邻区为占用块,右邻区为空闲块...\n", p->size);
|
||||
|
||||
h = p + p->size; // 右邻区的头部
|
||||
|
||||
f = FootLoc(h); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
f->uplink = p; // 释放块的头部将作为合并后新块的头部
|
||||
|
||||
p->tag = 0;
|
||||
p->size += h->size;
|
||||
|
||||
// 释放块的头部链接域要更新为与右邻区头部的链接域一致
|
||||
p->llink = h->llink;
|
||||
p->rlink = h->rlink;
|
||||
h->llink->rlink = p;
|
||||
h->rlink->llink = p;
|
||||
|
||||
// pav指向合并后的结点的新头部
|
||||
*pav = p;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 4.释放块的左、右邻区均为空闲块
|
||||
*
|
||||
* 此时需要合并左邻区、释放块、右邻区
|
||||
*/
|
||||
if(Ltag == 0 && Rtag == 0) {
|
||||
printf("日志:\"释放块\"的容量为 %d ,且它的左、右邻区均为空闲块...\n", p->size);
|
||||
|
||||
h = (p - 1)->uplink; // 左邻区的头部,这将成为合并后的新块的头部
|
||||
q = p + p->size; // 右邻区的头部
|
||||
f = FootLoc(q); // 右邻区的底部,这将成为合并后的新块的底部
|
||||
|
||||
h->size += p->size + q->size; // 合并后的新块大小
|
||||
f->uplink = h; // 新块底部信息也要更新
|
||||
|
||||
// 移除右邻区
|
||||
q->rlink->llink = q->llink;
|
||||
q->llink->rlink = q->rlink;
|
||||
|
||||
printf("日志:回收成功!空闲空间容量为 %d ...\n", AvailableSpace(*pav));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
Space p;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
p = av;
|
||||
count = av->size;
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("□");
|
||||
} else {
|
||||
printf("■");
|
||||
}
|
||||
|
||||
if(i == count && count < len) {
|
||||
p = p + p->size;
|
||||
count += p->size;
|
||||
printf("|");
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
// 每隔20个换一下行
|
||||
if(i % 20 == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(len % 20 != 0) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav) {
|
||||
Space p;
|
||||
int count;
|
||||
|
||||
if(pav == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = pav;
|
||||
count = 0;
|
||||
|
||||
do {
|
||||
count += p->size;
|
||||
p = p->rlink;
|
||||
} while(p != pav);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*==============
|
||||
* 边界标识法
|
||||
*
|
||||
* 包含算法: 8.1
|
||||
===============*/
|
||||
|
||||
#ifndef BOUNDARYTAGMETHOD_H
|
||||
#define BOUNDARYTAGMETHOD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define e 5 // 分配空间时用到的容差
|
||||
#define FootLoc(p) p+(p->size)-1 // 将p指针定位到p所指内存块的底部
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 教材中将llink和uplink封装在了一个联合体中,个人感觉这个封装有些鸡肋。
|
||||
* 一方面,head和foot的公共部分其实只有tag,如果真想用联合体,那么应当把size和rlink也封装起来。
|
||||
* 另一方面,这个封装节省的空间很有限,而且直接影响了代码的可读性。
|
||||
* 这里只是教学代码,而不是真实的系统代码,所以空间考虑在其次,原理展示为首要任务。
|
||||
* 此外,教材中的伪码中也并没有考虑这个联合体,而是直接进行操作的。
|
||||
* 综上所述,这里去除了教材中的联合体结构。
|
||||
* 如想观察带有联合体的写法,可以参考CFree分支的代码。
|
||||
*/
|
||||
|
||||
int tag; // 块标志,0空闲,1占用,头部和尾部均有
|
||||
|
||||
struct WORD* llink; // 头部域,指向前驱结点
|
||||
struct WORD* rlink; // 头部域,指向后继结点
|
||||
int size; // 头部域,块大小
|
||||
|
||||
struct WORD* uplink; // 底部域,指向本结点头部
|
||||
} WORD;
|
||||
|
||||
typedef WORD* Space; // Space:指向可利用空间的指针类型
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为n个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head和foot。
|
||||
*/
|
||||
Space InitSpace(int n);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.1 ████████
|
||||
*
|
||||
* 边界标识法的内存分配算法
|
||||
*
|
||||
* 从空间pav中申请一块大小至少为n的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
* 为了使分配后的剩余块尽量均匀分布,每次分配完之后都要把空间指针pav向前移动,具体描述参见教材。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.为了避免空间碎片化过快,这里增加了一个容差e,具体含义参考教材描述。
|
||||
* 3.这里申请分配n个字的空间,指的是已经完成换算的空间。
|
||||
* 比如用户想要容量为10个字的空间,但每个空间又包含head和foot这两个字存储空间使用信息,
|
||||
* 因此实际上分配的空间大小应为12个字,这个"12"就是n的含义。
|
||||
* 教材中提到"head和foot在分配时忽略不计",这样是为了伪码书写的方便。实际写代码时,这两个空间是不能忽略的。
|
||||
* 但是如果按照上面的描述,把n解释为已经换算后空间,而不是用户原始申请的空间,
|
||||
* 那么既满足了没有忽略空间,又满足了在书面效果上,跟教材伪码统一,可谓两全其美。
|
||||
*/
|
||||
Space AllocBoundTag(Space* pav, int n);
|
||||
|
||||
/*
|
||||
* 边界标识法的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放
|
||||
*/
|
||||
void FreeBoundTag(Space* pav, Space p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 计算可用的空闲空间容量
|
||||
*
|
||||
* 注:仅限内部使用,用在日志打印中
|
||||
*/
|
||||
static int AvailableSpace(Space pav);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{6485E7FA-6038-4C6F-A91F-3B1D5E8B532D}</ProjectGuid>
|
||||
<RootNamespace>My0802_BuddySystem</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(SolutionDir)\..\Status;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(SolutionDir)\..\Status\Status.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BuddySystem-main.c" />
|
||||
<ClCompile Include="BuddySystem.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BuddySystem.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BuddySystem.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BuddySystem-main.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BuddySystem.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
44
VisualC++/CourseBook/0802_BuddySystem/BuddySystem-main.c
Normal file
44
VisualC++/CourseBook/0802_BuddySystem/BuddySystem-main.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <stdio.h>
|
||||
#include "BuddySystem.h" //**▲08 动态存储管理**//
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
FreeList avail;
|
||||
WORD* p[8]; // 记录申请到的内存的指针
|
||||
int s[8] = {4, 5, 6, 7, 1, 5, 3, 9}; // 申请的空间大小
|
||||
int i;
|
||||
|
||||
printf("████████ InitSpace \n");
|
||||
{
|
||||
printf("初始化一个内存块...\n");
|
||||
InitSpace(avail);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ AllocBuddy \n");
|
||||
{
|
||||
for(i = 0; i < 8; i++) {
|
||||
printf("申请大小为 %d 个字的内存块...\n", s[i]);
|
||||
p[i] = AllocBuddy(avail, s[i]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
printf("████████ FreeBuddy \n");
|
||||
{
|
||||
// 定义一个指针回收顺序
|
||||
int a[8] = {2, 0, 5, 7, 1, 4, 3, 6};
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
FreeBuddy(avail, p[a[i]]);
|
||||
PrintMemoryLayout();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
PressEnterToContinue(debug);
|
||||
|
||||
return 0;
|
||||
}
|
||||
283
VisualC++/CourseBook/0802_BuddySystem/BuddySystem.c
Normal file
283
VisualC++/CourseBook/0802_BuddySystem/BuddySystem.c
Normal file
@@ -0,0 +1,283 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#include "BuddySystem.h"
|
||||
|
||||
// 记录内存的起始地址,在计算伙伴块时候需要用到
|
||||
WORD* start;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail) {
|
||||
int k;
|
||||
WORD* r;
|
||||
|
||||
// 遍历M+1个元素
|
||||
for(k = 0; k <= M; k++) {
|
||||
avail[k].nodesize = (int) pow(2, k);
|
||||
avail[k].first = NULL;
|
||||
}
|
||||
|
||||
r = (WORD*) malloc((int) pow(2, M) * sizeof(WORD));
|
||||
if(r == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 设置头部信息
|
||||
r->llink = r->rlink = r;
|
||||
r->tag = 0;
|
||||
r->kval = M;
|
||||
|
||||
avail[M].first = r;
|
||||
|
||||
start = r;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n) {
|
||||
int k, i;
|
||||
WORD* pa, * pre, * suc, * pi;
|
||||
|
||||
/*
|
||||
* 增加一个判断:如果换算后的空间容量小于1,则直接返回。
|
||||
*/
|
||||
if(n < 1) {
|
||||
printf("日志:分配失败!申请的\"字\"数应当不小于1\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 查找不小于n的空闲块
|
||||
for(k = 0; k <= M && (avail[k].nodesize < n + 1 || !avail[k].first); k++) {
|
||||
}
|
||||
|
||||
// 找不到合适的空闲块,返回空指针
|
||||
if(k > M) {
|
||||
printf("日志:分配失败!没有足够的空闲块\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pa = avail[k].first; // 指向可分配子表的第一个结点
|
||||
pre = pa->llink; // 分别记下前驱和后继
|
||||
suc = pa->rlink;
|
||||
|
||||
// 如果此处仅有一个空闲块,则分配后该子表变为空
|
||||
if(pa == suc) {
|
||||
avail[k].first = NULL;
|
||||
|
||||
// 否则,从链表头部摘下一个可用的空闲块,并将子表头指针指向下一个空闲块
|
||||
} else {
|
||||
pre->rlink = suc;
|
||||
suc->llink = pre;
|
||||
avail[k].first = suc;
|
||||
}
|
||||
|
||||
/*
|
||||
* 从k-1处开始逆向遍历FreeList数组,向其中填充剩余的空闲块。
|
||||
* 剩余的空闲块是对剩余可用空间的拆分。
|
||||
*
|
||||
* 这里用到一个公式:2^m-2^n = 2^n+2^(n+1)+...+2^(m-1)
|
||||
* 比如初始容量为2^16,此时总共申请1500个字,那么需要分配一块2^11的空闲块给它。
|
||||
* 分配完之后,剩余的容量为:2^16-2^11 = 2^11+2^12+2^13+2^14+2^15。
|
||||
* 这些剩余容量可以拆分为5个空闲块,分别存储到15、14、13、12、11这五个索引处。
|
||||
*/
|
||||
for(i = 1; k - i >= 0 && avail[k - i].nodesize >= n + 1; i++) {
|
||||
pi = pa + (int) pow(2, k - i); // 每次将pi指向剩余空间的后一半
|
||||
pi->rlink = pi->llink = pi; // 初始化pi的前驱和后继
|
||||
pi->tag = 0; // 标记为空闲块
|
||||
pi->kval = k - i; // 设置该块的容量标志,真实容量为2^(k-i)
|
||||
avail[k - i].first = pi;
|
||||
|
||||
/*
|
||||
* 注:
|
||||
* 上面分解出来的pi直接添加到了avail中,并没有考虑同位置处会不会有别的容量相同的空闲块。
|
||||
* 这里不需要考虑的原因是如果同位置处已经存在别的容量相同的空闲块,
|
||||
* 那么这里根本不需要分解剩余空间,换句话说,连这个循环都进不来。
|
||||
* 只要进来这个循环,说明目标位置处已经为空了,没有找到合适的空闲块,所以这才进一步向高游标处寻找空闲块。
|
||||
*/
|
||||
}
|
||||
|
||||
// 最后剩下的最靠前的空间就是需要分配的空间(这里没有设置pa的前驱和后继,因为没必要)
|
||||
pa->tag = 1;
|
||||
pa->kval = k - (--i);
|
||||
|
||||
printf("日志:分配成功!用户申请 %d 个字,系统申请 %d 个字,实际分配 %d 个字\n", n, n + 1, (int) pow(2, pa->kval));
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p) {
|
||||
int k;
|
||||
WORD* r;
|
||||
WORD* buddy = Buddy(p);
|
||||
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 这里将p结点简单地插入到avail中,包含三种情形:
|
||||
* 1.伙伴块非空闲
|
||||
* 2.伙伴空闲,但是伙伴的大小跟p的大小不一致,说明伙伴还没拼合好
|
||||
* 3.p拼接成了最后一个最大的空闲块
|
||||
*/
|
||||
if(buddy->tag == 1 || buddy->kval != p->kval || p->kval == M) {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找p结点应当进入的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,采用头插法将空闲块插入到目标插槽
|
||||
if(k <= M && k == p->kval) {
|
||||
p->tag = 0;
|
||||
|
||||
if(avail[k].first == NULL) {
|
||||
p->llink = p->rlink = p;
|
||||
} else {
|
||||
p->llink = avail[k].first->llink;
|
||||
p->rlink = avail[k].first;
|
||||
p->llink->rlink = p;
|
||||
p->rlink->llink = p;
|
||||
}
|
||||
|
||||
avail[k].first = p;
|
||||
|
||||
printf("日志:回收成功![%d, (2^%d)]进入插槽 %d 的空闲块链表上\n", (int) (p - start), k, k);
|
||||
}
|
||||
|
||||
// 如果伙伴块是空闲的,此时应当进行合并操作
|
||||
} else {
|
||||
for(k = 0; k <= M && k < p->kval; k++) {
|
||||
// 查找伙伴块所在的插槽
|
||||
}
|
||||
|
||||
// 找到插槽,将伙伴块从空闲块链表中摘下来
|
||||
if(k <= M && k == p->kval) {
|
||||
// 伙伴在链表第一个位置
|
||||
if(avail[k].first == buddy) {
|
||||
buddy->rlink->llink = buddy->llink;
|
||||
buddy->llink->rlink = buddy->rlink;
|
||||
|
||||
avail[k].first = buddy->rlink;
|
||||
|
||||
// 伙伴在中间位置
|
||||
} else {
|
||||
for(r = avail[k].first; r->rlink != buddy; r = r->rlink) {
|
||||
// 查找伙伴,r指向伙伴的前驱
|
||||
}
|
||||
|
||||
r->rlink = buddy->rlink;
|
||||
buddy->rlink->llink = r;
|
||||
}
|
||||
|
||||
printf("日志:合并成功![%d, (2^%d)]和[%d, (2^%d)]合并成了", (int) (p - start), k, (int) (buddy - start), k);
|
||||
|
||||
// 合并之前,需要确定哪个伙伴靠前
|
||||
if(p < buddy) {
|
||||
p->tag = 0;
|
||||
} else {
|
||||
p = buddy;
|
||||
}
|
||||
|
||||
p->kval = k + 1; // 指数增一后,即完成合并
|
||||
|
||||
printf("[%d, (2^%d)]\n", (int) (p - start), k + 1);
|
||||
|
||||
// 出现新的空闲块之后,要进入递归,查看该空闲块是否也存在空闲伙伴
|
||||
FreeBuddy(avail, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout() {
|
||||
int i, count, total;
|
||||
WORD* p;
|
||||
|
||||
printf("|");
|
||||
|
||||
p = start;
|
||||
count = (int) pow(2, p->kval);
|
||||
|
||||
for(i = 1; i <= count; i++) {
|
||||
if(p->tag == 0) {
|
||||
printf("_");
|
||||
} else {
|
||||
printf("*");
|
||||
}
|
||||
|
||||
// 进入到下一个块
|
||||
if(i == count && count < (int) pow(2, M)) {
|
||||
p = start + count;
|
||||
count += (int) pow(2, p->kval);
|
||||
printf("|");
|
||||
}
|
||||
}
|
||||
|
||||
printf("|\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p) {
|
||||
long s, m, n;
|
||||
|
||||
if(p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// start是整个空闲块的绝对起始地址,s是p在伙伴系统中的绝对地址,从0开始
|
||||
s = p - start;
|
||||
if(s < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m = (long) pow(2, p->kval);
|
||||
n = (long) pow(2, p->kval + 1);
|
||||
|
||||
if(s % n == 0) {
|
||||
return p + m;
|
||||
}
|
||||
|
||||
if(s % n == m) {
|
||||
return p - m;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
89
VisualC++/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
89
VisualC++/CourseBook/0802_BuddySystem/BuddySystem.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*==============
|
||||
* 伙伴系统
|
||||
*
|
||||
* 包含算法: 8.2
|
||||
===============*/
|
||||
|
||||
#ifndef BUDDYSYSTEM_H
|
||||
#define BUDDYSYSTEM_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
|
||||
/* 宏定义 */
|
||||
#define M 6 // 假设总空间大小为2^M个字,子表个数为M+1
|
||||
|
||||
|
||||
/*
|
||||
* 内存"字"的类型定义
|
||||
*
|
||||
* 所谓"字"是指空间分配的最小单位,它不是字节。
|
||||
* 一个"字"有多大,取决于对"字"结构是如何定义的。
|
||||
*/
|
||||
typedef struct WORD {
|
||||
struct WORD* llink; // 头部域的前驱指针
|
||||
struct WORD* rlink; // 头部域的后继指针
|
||||
int tag; // 头部域的块标志,0:空闲,1:占用
|
||||
int kval; // 指示块的大小,比如其值为K时,表示该块的大小为2^K
|
||||
} WORD;
|
||||
|
||||
// 表头向量类型
|
||||
typedef struct HeadNode {
|
||||
int nodesize; // 该链表的空闲块的大小
|
||||
WORD* first; // 该链表的表头指针
|
||||
} FreeList[M + 1];
|
||||
|
||||
|
||||
/*
|
||||
* 初始化一块大小为2^M个字的内存,并返回指向该内存的起点的指针
|
||||
* 注:返回的初始内存已经包含了head。
|
||||
*/
|
||||
void InitSpace(FreeList avail);
|
||||
|
||||
/*
|
||||
* ████████ 算法8.2 ████████
|
||||
*
|
||||
* 伙伴系统的内存分配算法
|
||||
*
|
||||
* 从空间avail中申请一块大小至少为n(原始值)的空间,并返回指向申请到的空间的指针。如果分配失败,则返回NULL。
|
||||
*
|
||||
* 注:
|
||||
* 1.这里采用首次拟合法,即一遇到满足条件的内存块就进行分配操作。
|
||||
* 2.这里申请分配n个字的空间,指的是用户申请的原始空间。
|
||||
* 实际在申请时,还要考虑到每个块前面有1个字的head信息,即经过换算后,实际需要申请(n+1)个字。
|
||||
* 这里的n与算法8.1里面的n含义正好相反,需要注意。
|
||||
*/
|
||||
WORD* AllocBuddy(FreeList avail, int n);
|
||||
|
||||
/*
|
||||
* 伙伴系统的内存回收算法
|
||||
*
|
||||
* 对指针p处的内存进行释放(这类似于free(),只是对内存进行释放操作,至于置空指针p的操作,应交给调用方完成)
|
||||
*
|
||||
* 注:这里没有验证p的取值,调用方应当确保p在合规的范围
|
||||
*/
|
||||
void FreeBuddy(FreeList avail, WORD* p);
|
||||
|
||||
/*
|
||||
* 打印内存布局,查看当前内存的使用情况
|
||||
* 注:仅限内部测试使用
|
||||
*/
|
||||
void PrintMemoryLayout();
|
||||
|
||||
/*
|
||||
* 查找块p的伙伴
|
||||
*
|
||||
* 将一个空闲块对半分裂后,会生成的两个小空闲块,这两个小空闲块互为伙伴。
|
||||
*
|
||||
* 计算伙伴的算法为:
|
||||
* 对于起始地址为p,大小为2^k的内存块:
|
||||
* 1.若 p MOD 2^(k+1) == 0 ,则p的伙伴块的起始地址为p+2^k,
|
||||
* 2.若 p MOD 2^(k+1) == 2^k ,则p的伙伴块的起始地址为p-2^k。
|
||||
*
|
||||
* 注:仅限内部使用,用在回收算法中
|
||||
*/
|
||||
static WORD* Buddy(WORD* p);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{66EA62B4-2F77-4BE6-AA24-C713EB7771DD}</ProjectGuid>
|
||||
<RootNamespace>My0803_GarbageCollection</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>$(SolutionDir)\..\Status;$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(SolutionDir)\..\Status\Status.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GarbageCollection-main.c" />
|
||||
<ClCompile Include="GarbageCollection.c" />
|
||||
<ClCompile Include="GList-HT.c" />
|
||||
<ClCompile Include="SString.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GarbageCollection.h" />
|
||||
<ClInclude Include="GList-HT.h" />
|
||||
<ClInclude Include="SString.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="GarbageCollection.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GarbageCollection-main.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GList-HT.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="SString.c">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="GarbageCollection.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GList-HT.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SString.h">
|
||||
<Filter>头文件</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
201
VisualC++/CourseBook/0803_GarbageCollection/GList-HT.c
Normal file
201
VisualC++/CourseBook/0803_GarbageCollection/GList-HT.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#include "GList-HT.h" //**▲05 数组和广义表**//
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L) {
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
*L = NULL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S) {
|
||||
SString emp; // 代表空广义表的字符串
|
||||
SString hsub, sub;
|
||||
GList p, q;
|
||||
|
||||
if(L == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
// 清理字符串S中的空白,包括清理不可打印字符和清理空格
|
||||
ClearBlank(S);
|
||||
|
||||
if(StrEmpty(S)) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
StrAssign(emp, "()");
|
||||
|
||||
/*
|
||||
* 如果输入串为(),则代表需要创建空的广义表
|
||||
*
|
||||
*【注】
|
||||
* 教材这里的代码是有问题的。
|
||||
* StrCompare的返回值指示的是两个字符串的大小,而不是指示两个字符串是否相等。
|
||||
* 如果给定的S与()相等,返回值应当是0。
|
||||
*/
|
||||
if(!StrCompare(S, emp)) {
|
||||
*L = NULL;
|
||||
} else {
|
||||
*L = (GList) malloc(sizeof(GLNode));
|
||||
if(*L == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
(*L)->mark = 0;
|
||||
|
||||
// 创建原子
|
||||
if(StrLength(S) == 1) {
|
||||
(*L)->tag = Atom;
|
||||
(*L)->Node.atom = S[1];
|
||||
} else {
|
||||
(*L)->tag = List;
|
||||
|
||||
p = *L;
|
||||
|
||||
// 去掉最外层括号
|
||||
SubString(sub, S, 2, StrLength(S) - 2);
|
||||
|
||||
// 重复建n个子表
|
||||
do {
|
||||
// 从sub中分离出表头串hsub,分离完成后,sub也会发生变化
|
||||
sever(hsub, sub);
|
||||
|
||||
// 递归创建广义表
|
||||
CreateGList(&(p->Node.ptr.hp), hsub);
|
||||
|
||||
q = p;
|
||||
|
||||
// 如果表尾不为空,需要继续处理表尾
|
||||
if(!StrEmpty(sub)) {
|
||||
p = (GList) malloc(sizeof(GLNode));
|
||||
if(p == NULL) {
|
||||
exit(OVERFLOW);
|
||||
}
|
||||
|
||||
// 初始化标记为0,意思是该结点未访问
|
||||
p->mark = 0;
|
||||
|
||||
p->tag = List;
|
||||
|
||||
q->Node.ptr.tp = p;
|
||||
}
|
||||
} while(!StrEmpty(sub));
|
||||
|
||||
q->Node.ptr.tp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L) {
|
||||
Print(L, Head);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark) {
|
||||
// L为空
|
||||
if(L == NULL) {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
}
|
||||
|
||||
printf(")");
|
||||
|
||||
// L不为空时
|
||||
} else {
|
||||
// 对于原子结点,输出原子
|
||||
if(L->tag == Atom) {
|
||||
printf("%c", L->Node.atom);
|
||||
|
||||
// 对于子表结点,要对表头、表尾分别讨论
|
||||
} else {
|
||||
if(mark == Head) {
|
||||
printf("(");
|
||||
} else {
|
||||
printf(",");
|
||||
}
|
||||
|
||||
Print(L->Node.ptr.hp, Head);
|
||||
Print(L->Node.ptr.tp, Tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str) {
|
||||
int i, k, n;
|
||||
SString ch;
|
||||
|
||||
n = StrLength(str);
|
||||
|
||||
i = 0; // 遍历字符串时的游标
|
||||
k = 0; // 标记遇到的未配对括号数量
|
||||
|
||||
do {
|
||||
++i;
|
||||
|
||||
// 截取str第一个字符
|
||||
SubString(ch, str, i, 1);
|
||||
|
||||
if(ch[1] == '(') {
|
||||
++k;
|
||||
}
|
||||
|
||||
if(ch[1] == ')') {
|
||||
--k;
|
||||
}
|
||||
} while(i < n && (ch[1] != ',' || k != 0));
|
||||
|
||||
// 如果存在多个广义表结点
|
||||
if(i < n) {
|
||||
SubString(hstr, str, 1, i - 1);
|
||||
SubString(str, str, i + 1, n - i);
|
||||
|
||||
// 只有一个广义表结点
|
||||
} else {
|
||||
StrCopy(hstr, str);
|
||||
ClearString(str);
|
||||
}
|
||||
}
|
||||
96
VisualC++/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
96
VisualC++/CourseBook/0803_GarbageCollection/GList-HT.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*============================
|
||||
* 广义表的头尾链表存储表示
|
||||
*
|
||||
* 包含算法: 5.5、5.6、5.7、5.8
|
||||
=============================*/
|
||||
|
||||
#ifndef GLIST_HT_H
|
||||
#define GLIST_HT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // 提供 malloc、realloc、free、exit 原型
|
||||
#include "Status.h" //**▲01 绪论**//
|
||||
#include "SString.h" //**▲04 串**//
|
||||
|
||||
/* 原子元素类型 */
|
||||
typedef char AtomType;
|
||||
|
||||
/*
|
||||
* 广义表结点标记
|
||||
*
|
||||
* Atom-0:原子结点
|
||||
* List-1:表结点
|
||||
*/
|
||||
typedef enum { Atom, List } ElemTag;
|
||||
|
||||
/* 广义表(头尾链表存储表示)类型定义 */
|
||||
typedef struct GLNode {
|
||||
int mark; // 为第8章第5节新增的变量,用来给广义表打上遍历标记,会在创建中初始化为0
|
||||
|
||||
ElemTag tag; // 公共标记,用于区分原子结点和表结点
|
||||
|
||||
// 原子结点和表结点的联合部分
|
||||
union {
|
||||
AtomType atom; // atom是原子结点的值域,AtomType由用户定义
|
||||
struct {
|
||||
struct GLNode* hp; // 指向表头
|
||||
struct GLNode* tp; // 指向表尾
|
||||
} ptr; // 表结点的指针域
|
||||
} Node;
|
||||
} GLNode;
|
||||
|
||||
/* 广义表类型 */
|
||||
typedef GLNode* GList;
|
||||
|
||||
/*
|
||||
* 图形化输出标记
|
||||
*
|
||||
* Head代表广义表指针来自表头
|
||||
* Tail代表广义表指针来自表尾
|
||||
*/
|
||||
typedef enum { Head, Tail } Mark;
|
||||
|
||||
|
||||
/*
|
||||
* 初始化
|
||||
*
|
||||
* 初始化空的广义表,长度为0,深度为1。
|
||||
*
|
||||
*【注】
|
||||
* 需要对每一层去掉括号考察
|
||||
*/
|
||||
Status InitGList(GList* L);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.7 ████████
|
||||
*
|
||||
* 创建
|
||||
*
|
||||
* 由字符串S创建广义表L。
|
||||
*/
|
||||
Status CreateGList(GList* L, SString S);
|
||||
|
||||
/*
|
||||
* 图形化输出
|
||||
*
|
||||
* 带括号输出广义表L。
|
||||
*/
|
||||
void PrintGList(GList L);
|
||||
|
||||
/*
|
||||
* 图形化输出的内部实现,mark是图形化输出标记。
|
||||
*/
|
||||
static void Print(GList L, Mark mark);
|
||||
|
||||
/*
|
||||
* ████████ 算法5.8 ████████
|
||||
*
|
||||
* 将非空串str分割成两部分:hsub为第一个','之前的子串,str为第一个','之后的子串。
|
||||
*
|
||||
*【注】
|
||||
* 1.这里假设字符串str输入正确,其中无空白符号,且str【已经脱去最外层括号】。
|
||||
* 2.分离完成后,str也会发生变化
|
||||
*/
|
||||
static void sever(SString hstr, SString str);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user