首页 > 系统相关 >动态内存管理——通讯录

动态内存管理——通讯录

时间:2023-03-20 14:31:59浏览次数:55  
标签:sz malloc 管理 memblock 通讯录 动态内存 con data size

本文主要介绍通讯录的动态实现,静态版可见此文——>静态通讯录,文章主要介绍了两次实现的不同,以及涉及到的基本知识。希望可以帮助到大家。

一、动态内存函数

1、申请函数

a、 malloc

笔者认为,malloc即memory allocate,也就是内存分配; image.png

功能

void* malloc (size_t size); malloc会在栈区申请一片连续空间,若申请成功并返回一个指向这边地址的指针,若失败则会返回NULL;

使用

由于申请失败会返回NULL,故每次malloc后,都需要判断是否申请成功; 返回指针是无类型的,故在使用malloc时,需自己指定类型,此处采用无类型,也是为了保证malloc可以适用于任何类型的对象; 申请空间size即便是0,malloc依旧会返回一个申请空间为0的地址,只不过这个地址不可以正常使用;  

b、calloc

笔者认为,calloc,即clean memory allocate;直译就是干净的内存分配,也就是说,calloc会将申请到的空间初始化,使其变得clean; image.png void* calloc(size_t num, size_t size);

功能

相比malloc,calloc的参数不同,作用也有些许差别,calloc会申请一篇空间为num个size字节大小的空间,若成功返回指向这片地址起始位置的指针,若失败也返回NULL;

使用

calloc相比malloc,

  • 最大的差别便是calloc会将申请的内存初始化为0;
  • 其次便是参数略有不同,malloc会申请size字节的空间,其参数size指的是申请空间的size; 而calloc会申请num*size字节的空间,其num指的是元素的个数,size指的是每个元素的大小;
  • 其余使用同malloc;

2、释放函数free

free即空闲,笔者认为,将原本的内存空闲掉,也就是释放内存; image.png void free( void* memblock );

功能

free会memblock指向的内存进行释放;

使用
  • free中的参数memblock可以是NULL,但此时函数立马返回,什么都不发生;
  • 否则,memblock必须时动态申请内存的起始地址,这样才可以将memblock指向的申请空间进行释放,否则函数会报错;
  • 需要注意的是,若需要减少原本分配的内存,也不可采用如下方法: 使用free,而将memblock往后移。 memblock只能是动态内存的起始地址,否则free会报错;若要调整空间,可以使用接下来介绍的realloc;

3、调整函数realloc

在英语中,re-词根常有一再、重新的意思,笔者认为此处便是采用re词根意思,实际上,realloc也可以理解成reallocate,重新分配的意思; image.png void* realloc(void* memblock, size_t size)

功能
  • realloc会将memblock指向的内存重新分配为size大小,若申请成功,则会返回指向这片空间的指针,否则会返回NULL;
  • 若申请的空间为0,同样会返回NULL,此时函数功能同free,会将memblock指向的动态内存进行释放;
  • 若memblock为NULL,则函数功能同malloc;
使用
  • 使用realloc时,其中的参数memblock,除NULL之外,必须是动态申请空间的起始位置;
  • realloc在申请内存时,若为增大空间,而原memblock申请的空间后已没有足够的空间,则,malloc会在内存中寻找新的size字节大小的空间,并将原数据迁移到新空间,并返回指向这边空间的地址;
  • 其余使用同malloc。

二、应用实例——通讯录的调整

静态实现版在此文中已有介绍——>静态通讯录,此处主要引入动态内存的使用,从而产生的修改——增加,删除,初始化,以及增加的退出,其余功能的实现与静态版相同。

1、结构体的修改

个人的结构体不用修改,而联系表的则有原本的数组,换成指针;另外增加一个参数用来确定当前容量的大小,具体原因见下方函数修改;

////联系表结构体静态版
//typedef struct Contact
//{
//	Peo data[MAX];
//	int sz;
//}Contact;
//联系表结构体
typedef struct Contact
{
	Peo* data;
	int sz;
	int capacity;//容量
}Contact;

2、函数的修改

a、初始化

在静态版本中,初始化借用memset将数组中每个元素都初始化为0; 而此处实现动态的增加,则需要申请新的空间,随之便有一个问题: 申请多少?什么时候申请呢? 由此我们定义一个参数容量,capacity,当联系表中个数sz等于容量capacity时,扩容; 初始化,我们申请3个空间,之后每次扩容增加2个;

//初始化动态
//初始为3,后续每次增加2
void Init_Contact(Contact* con)
{
	assert(con);
	con->sz = 0;
	con->capacity = 3;
	con->data = (Peo*)malloc(sizeof(Peo) * con->capacity);
}
//扩容
void Enlarge(Contact* con)
{
	con->capacity += 2;
	Peo* tmp = NULL;
	tmp = (Peo*)realloc(con->data, sizeof(con->capacity));
	if (tmp != NULL)
	{
		con->data = tmp;
		printf("扩容成功\n");
	}
	else
		printf("扩容失败\n");
}

b、增加联系人

借助扩容,原本用于判断数组是否满的语句,现在只需扩容即可;

//增加联系人动态
void Add_Contact(Contact* con)
{
	assert(con);
	if (con->sz == con->capacity)
	{
		//扩容
		Enlarge(con);
	}
	printf("请输入名字:");
	scanf("%s", con->data[con->sz].name);
	printf("请输入性别:");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &(con->data[con->sz].age));
	printf("请输入电话:");
	scanf("%s", con->data[con->sz].tel);
	printf("请输入地址:");
	scanf("%s", con->data[con->sz].addr);
	con->sz++;
	printf("添加成功!\n");
}

c、删除联系人

相比静态,删除后,增加判断语句,是否容量比个数多2个,若多,则减少容量,调整分配空间;

//按名字删除动态
void Del_By_Name(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空!不可删除\n");
		return;
	}
	char cmp[20] = { 0 };
	printf("请输入要删除的人:");
	scanf("%s", cmp);
	int x = Search_By_Name(con, cmp);
	if (x != -1)
	{
		for (; x < con->sz; x++)
		{
			con->data[x] = con->data[x + 1];
		}
		con->sz--;
		printf("删除成功!\n");
		if (con->capacity - con->sz == 2)
		{
			con->capacity -= 2;
			con->data = (Peo*)realloc(con->data, con->capacity);
		}
	}
	else
	{
		printf("要删除的人不存在!\n");
	}
}

d、增加删除语句

此处增加的删除语句,主要用来释放申请的空间,避免内存泄漏;

void Exit(Contact* con)
{
	free(con->data);
	con->data = NULL;
	con->capacity = 0;
	con->sz = 0;
}

汇总的代码以上传到gitee,有需要的话,可以点链接直达——>通讯录代码

标签:sz,malloc,管理,memblock,通讯录,动态内存,con,data,size
From: https://blog.51cto.com/u_15423682/6132764

相关文章

  • 配置管理
    一部分配置会经常发生修改,比如限流降级开关配置、业务中的白名单配置等。这些配置项除了变更频繁,还要求实时性,如果采取和应用一起发布的方式,那么每次变更都要重新发布服务,......
  • linux目录——文件管理
    个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。座右铭:海不辞水,故能成其大;山不辞石,故能成其高。个人主页:​​小李会科技的主页​​......
  • 状态管理介绍 pinia
    什么是状态管理?理论上来说,每一个Vue组件实例都已经在“管理”它自己的响应式状态了。<scriptsetup>import{ref}from'vue'//状态constcount=ref(0)//......
  • 设计师必备:免费素材管理工具Billfish v3.0更新了!
    ​​Billfish是专门为设计师打造的图片收藏管理工具,可以轻松管理您的各种素材文件。Billfish是一个免费的软件,支持对大量的图片素材进行管理,提供多种快速的检索筛选功能,如......
  • pinia 使用状态管理
    一、定义状态变量、方法在src\stores\ 目录下,新建状态管理文件counter.tsimport{ref,computed}from'vue'import{defineStore}from'pinia'exportconst......
  • 一款开源的 Kafka 管理平台
    项目地址:https://github.com/provectus/kafka-ui 我是通过docker-compose安装的 操作过程可以会遇到地址解析问题,可参考文章:https://www.cnblogs.com/ReturnOfTheKi......
  • 版本管理工具git介绍与使用
    Git简介Git是一个分布式版本控制系统,它可以跟踪文件的变化并记录每个版本的修改历史。Git的原理是将文件的变化记录在一个称为“仓库”的地方,并允许用户在不同的分支上进......
  • 【C版本】静态通讯录与动态通讯录的实现,以及各自所存在的缺陷对比。(含所有原码)
    @​​TOC​静态版本通讯录前期思路与之前的扫雷以及三子棋的实现方式是一样的,创建两个源文件,一个用来测试,一个用来存放函数定义,再创建一个头文件,用来存放函数声明。接下来是......
  • 用C语言实现通讯录(初级版本)全部代码
    注意:分别用test.c(主函数),contact.c(函数实现),contact,h(函数声明)实现代码test.c#define_CRT_SECURE_NO_WARNINGS1#include"contact.h"voidmenu(){printf("**************......
  • 容量管理的三个层次
    ITIL把容量管理分为三个层次:业务层(BusinessLevel)、服务层(ServiceLevel)和组件层(ComponentLevel)。这三个层次的流程中有许多类似的活动,但是每个子流程有不同的侧重点。业务......