动态空间的开辟

动态空间,往往是伴随着结构体出现,在数据结构中有着重要的地位

例如:

【问题描述】

在操作系统中,空闲存储空间通常以空闲块链表方式组织,每个块包含块起始位置、块长度及一个指向下一块的指针。空闲块按照存储位置升序组织,最后一块指向第一块(构成循环链表)。当有空间申请请求时,按照如下原则在空闲块循环链表中寻找并分配空闲块:

1)从当前位置开始遍历空闲块链表(初始是从地址最小的第一个空闲块开始),寻找满足条件的最小块(即:大于等于请求空间的最小空闲块,如果有多个大小相同的最小空闲块,则选择遍历遇到的第一个空闲块)(最佳适应原则);

2)如果选择的空闲块恰好与请求的大小相符合,则将它从链表中移除并返回给用户;这时当前位置变为移除的空闲块指向的下一空闲块;

3)如果选择的空闲块大于所申请的空间大小,则将大小合适的空闲块返回给用户,剩下的部分留在空闲块链表中;这时当前位置仍然为该空闲块;

4)如果找不到足够大的空闲块,则申请失败;这时当前位置不变。

例如:下图示例给出了空闲块链表的初始状态,每个结点表示一个空闲块,结点中上面的数字指空闲块的起始位置,下面的数字指空闲块的长度,位置和长度都用正整数表示,大小不超过int表示范围。当前位置为最小地址为1024的空闲块。

若有4个申请空间请求,申请的空间大小依次为:1024、2560、10240和512。则从当前位置开始遍历上图的链表,按照上述原则寻找到满足条件的最小块为地址是16384的空闲块,其长度正好为1024,所以将其从链表中删除,这时链表状态如下图所示,当前位置变成地址为32768的空闲块。

从当前位置开始为第二个空间请求(大小为2560)遍历链表,按照上述原则寻找到满足条件的最小块为地址是80896的空闲块,其长度为3072,大于请求的空间大小,于是申请空间后该空闲块剩余的长度为512,当前位置为地址是80896的空闲块,链表状态如下图所示:

从当前位置开始为第三个空间请求(大小为10240)遍历链表,遍历一圈后发现找不到足够大的空闲块,则忽略该请求,当前位置不变。下面继续为第四个空间请求(大小为512)遍历链表,按照上述原则寻找到满足条件的最小块为当前位置的空闲块,其长度等于请求的空间大小,于是将该空闲块删除后,链表状态变为下图所示:

编写程序,模拟上述空闲空间申请。

【输入形式】

先从控制台读入一正整数,表示当前空闲块的个数(大于0且小于等于100)。

然后按照起始位置由小到大的顺序分行输入每个空闲块的起始位置和长度,位置和长度都用正整数表示,大小不超过int表示范围,两整数间以一个空格分隔。

最后在新的一行上依次输入申请空间的大小,以-1表示结束,各整数间以一个空格分隔,申请请求的个数不超过100个。

【输出形式】

按照上述原则模拟完空闲空间申请后,输出当前空闲空间链表状态,即从当前位置开始,遍历链表,分行输出剩余空闲块的起始位置和长度,位置和长度间以一个空格分隔。若申请完后,链表中没有空闲块,则什么都不输出。
【样例输入】

12

1024 2048

8192 512

16384 1024

32768 8192

65536 8192

77824 1024

80896 3072

86016 1024

91136 5120

99328 512

104448 1024

112640 3072

1024 2560 10240 512 1024 6400 512 -1
【样例输出】

104448 1024

112640 3072

1024 2048

8192 512

32768 1792

65536 8192

77824 1024

91136 5120
【样例说明】

样例输入了12个空闲块的信息,形成了如上述第一个图所示的空闲块链表;然后读取了7个空间申请请求,为前4个申请请求分配空间后,空闲块链表状态为上述最后一张图所示。满足第五个请求后,将删除地址为86016的空闲块;满足第六个请求后,地址为32768的空闲块剩余长度为1792;满足第七个请求后,将删除地址为99328的空闲块,这时链表中剩余8个空闲块,当前位置为地址是104448的空闲块,从该空闲块开始依次遍历输出所有剩余空闲块的起始位置和长度。
【评分标准】

该题要求编程模拟实现空闲空间的申请,提交程序名为memory.c。

#include<stdio.h>
#include<stdlib.h>
struct node {
    int dizhi; 
    int daxiao; 
    struct node* next;
	struct node* front; 
};

void printFreeBlocks(struct node* head,int num);

int main()
{
    struct node* list = NULL;
    struct node* chushi = list;
    int n = 0;
    int i = 0;
    scanf("%d", &n);
    int u = n;
    // 读入空闲块的起始位置和长度,构建链表
    while (n--)
    {
        struct node* newNode = (struct node*)malloc(sizeof(struct node));
        scanf("%d", &newNode->dizhi);
        scanf("%d", &newNode->daxiao);
        newNode->next = NULL;
        if (list == NULL)
        {
            list = newNode;
            chushi = list;
        }
        else
        {
            list->next = newNode;
            newNode->front = list; // 设置新节点的前向指针
            list = list->next;
        }
    }
    chushi->front = list;
    list->next = chushi;
    n = u;
    struct node* head = chushi;
    struct node* temp = chushi->next;

    while (temp != chushi) 
    {
        if (temp->dizhi < head->dizhi) 
        {
            head = temp;
            
        }
        temp = temp->next;
    }
    chushi = head;
    while (scanf("%d", &i) != EOF && i != -1)
    {
        struct node* current = chushi;
        struct node* prev = current->front; 
        struct node* suan=NULL;
        int found = 0;
        int w = u;
        while (w--)
        {
            if (current->daxiao >= i)
            {
                found = 1;
                int shu=u;
                suan=current->next;
                while(shu--)
                {
                    if(suan->daxiao>=i&&suan->daxiao<current->daxiao)
                    {
                    	prev=suan->front; 
                        current=suan;
                    }
                    suan=suan->next;
                }
                break;
            }
            prev = current;
            current = current->next;
        }

        if (found)
        {
            current->daxiao -= i;
            chushi=current;
            if (current->daxiao == 0)
            {
                chushi = current->next;
                prev->next = current->next;
                current->next->front = prev; // 更新后继节点的前向指针
                free(current);
                current = NULL;
                u--;
            }
        }
    }
    
    printFreeBlocks(chushi, u);
    
    return 0;
}

void printFreeBlocks(struct node* head, int num)
{
    while (num--)
    {
        printf("%d %d\n", head->dizhi, head->daxiao);
        head = head->next;
    }
}

在本题中,就很明显的用了malloc函数来申请结构体的空间,但是内存空间函数应该怎么去用呢?

内存空间函数一共有三种,分别是malloc,calloc,realloc

该三个函数均包含在头文件<stdlib>

1.malloc

void* malloc(size_t size) ;
#include<stdlib.h>
#include<stdio.h>
int main()
{
	//假设开辟10个整形的空间
	int *p=(int *)malloc(10*sizeof(int)) ;//malloc默然类型是void*需要强制类型转换为int*
	//使用这些空间的时候
	if(p==NULL)
	{
		printf("malloc error\n");
		return 0;
	 } 
	 //使用
	 int i=0;
	 for(i=0;i<10;i++)
	 {
	 	*(p+i)=i;
	  } 
	for(i=0;i<10;i++)
	{
		printf("%d ",p[i]);
	}
	//回收空间
	free(p);//free只能释放动态开辟的内存 malloc与free一定要成对出现 
	p=NULL; 
	return 0;
}

在malloc的原函数中,理论上只需要传入size,即开辟空间的大小即可,但是在该题中,malloc却是

int *p=(int *)malloc(10*sizeof(int)) ;

(int* )是为了将malloc开辟出来的空间强行类型转化成int*类型,但其实这一步并不是必要的(在有些编译器中没有类型转化也可以,(因为p已经被定义成int* 类型)但是为了保险起见最好写上,毕竟malloc的原函数类型是void*,也不知道会转化成什么样子)。

用malloc给整数指针开辟空间看似是很好理解,这其实和数组差不多,上述代码可以简写成int p[10].

不过,当开辟空间的函数用于结构体,似乎就不那么合理了

例如在上述例题中用到了如下代码:

struct node* newNode = (struct node*)malloc(sizeof(struct node));

就是给struct node*类型的指针newNode一块开辟大小为struct node,类型为struct node *的空间,用于存储其中数据。

但是为什么要开辟呢,直接定义一个struct node*  newNode不好吗?

如果直接开辟struct node*  newNode,系统只会给到newNode8个字节大小的空间,但是我们好好数一下结构体的大小就会发现,该结构体如果要存储全部的数据,那么需要24个字节,如果只有8个字节,那么应该如何分配呢?所以结构体指针在定义的时候一定要记得为其,开辟空间,如果忘记开辟,数据很有可能就无法进行正常的存储。

2.calloc

void *calloc(size_t num,size_t size);//num元素的个数,size每个元素的长度,并可以将每个元素初始化为0

calloc与malloc的区别:

calloc在开辟空间时,会将每个元素都初始化为0(如果是指针就将指针赋值为NULL),而malloc不会

(但是其实malloc更常用,因为当我们开辟出来一块空间,下一步紧接着就是去使用这一块空间,如果用calloc开辟,会有赋值为0的过程,而这个过程是消耗时间的)

#include<stdio.h>
#include<stdlib.h>

int main()
{
	//假设开辟10个整形的空间
	int *p=(int *)calloc(10,sizeof(int)) ;//calloc默然类型是void*需要强制类型转换为int*
	//使用这些空间的时候
	if(p==NULL)
	{
		printf("malloc error\n");
		return 0;
	 } 
	for(i=0;i<10;i++)
	{
		printf("%d ",p[i]);//每个元素打印出来都是0 
	}
	//回收空间
	free(p);//free只能释放动态开辟的内存 malloc与free一定要成对出现 
	p=NULL; 
	return 0;
}

3.realloc

void *realloc(void* ptr,size_t size);//ptr是之前开辟的动态空间的地址 

realloc是来给一个指针重新来分配空间,而保留原空间中的内容(如果新空间小于原空间,原来的数据当然会丢失)与前面两个空间开辟函数不同的是,realloc需要传入指针ptr

ptr是需要来重新分配空间的指针,size是重新开辟的空间的大小

int main()
{
	//假设开辟10个整形的空间
	int *p=(int *)calloc(10,sizeof(int)) ;//calloc默然类型是void*需要强制类型转换为int*
	//使用这些空间的时候
	if(p==NULL)
	{
		printf("malloc error\n");
		return 0;
	 } 
	for(i=0;i<10;i++)
	{
		*(p+i)=5 ;
	}
	//这里需要的p指向的空间更大,需要20个int的空间
	//realloc调整空间,可以将旧的空间释放掉,并将旧空间中的数据移动到新空间中 
	int* ptr=(int *)realloc (p,20*sizeof(int ));//接受时不能再使用p来接受
	//realloc有可能找不到合适的空间来调整大小,这时就会返回NULL 
	if(ptr!=NULL)
	{
		p=ptr;
	 } 
	//回收空间
	free(p);//free只能释放动态开辟的内存 malloc与free一定要成对出现 
	p=NULL; 
	return 0;
}

但是realloc的返回值往往不再用原指针p来接受,而是要定义一个新指针来接受,目的是为了当开辟的空间不合理时,会导致新空间开辟失败,被改为NULL,导致原来空间空间中1的数据丢失。

所以往往是在判断用来接受的新指针不等于NULL时,再将新指针的地址赋给原指针

注:realloc单独使用时也能有malloc的效果

开辟方法如下:

int *p=(int *)realloc(NULL,10*sizeof(int));

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值