C语言动态内存讲解+通讯录2.0

文章目录

前文

本文主要介绍动态开辟的几个函数,以及改进之前的通讯录。

我们局部变量等是在栈区上开辟空间的,而我们动态开辟的空间在堆上面。

头文件: #include<stdlib.h>

函数介绍:

动态开辟空间的函数有以下四个,下面由我来一一介绍
malloc
calloc
realloc
free

malloc和free

malloc是开辟一块连续的空间

用法如下,需要注意的是,由于malloc 是动态开辟内存的,所以我们需要判断是否开辟成功,如果是NULL 就说明开辟失败了,malloc如果开辟失败了就会返回NULL;
free则是专门释放由动态开辟的空间的。

c 复制代码
#include <stdlib.h>
int main()
{
	int* arr = (int*)malloc(INT_MAX);
	if (arr == NULL)
	{
		perror("malloc arr");
		return 1;
	}
	int  i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	free(arr);
	arr = NULL;
	return 0;
}

下面我们通过调试来开一下free的过程

这里可以看到当我们malloc完后,arr就指向了一块有10个整型大小的连续空间

当我们free完之后发现虽然我们的空间已经被收回了,但是arr此时还是保留 着那片空间的地址,此时arr就是野指针 ,不能 继续使用,所以每当我们使用完动态开辟的空间free之后,我们都应该手动 的把这块空间置为NULL

接下来验证以下malloc开辟失败 的情况。

我们把malloc的参数设置为INT_MAX ,INT_MAX表示整型所能容纳的最大整数 ,在x86的环境上,此时就是INT_MAX 就是我们所拥有的最大空间 ,但不可能把所有的内存 都给arr ,所以开辟失败 ,此时arr指向NULL

calloc

calloc和malloc的区别就是calloc开辟的空间是初始化过了的。

同时calloc开辟的空间用完以后,也要free掉,同时置为NULL。

realloc

realloc是当空间大小不够的时候,可以在增加空间的大小 ,当然也可以减少空间

同时realloc开辟空间分两种情况如图:

c 复制代码
#include <stdlib.h>

int main()
{
	int* arr_malloc = (int*)malloc(40);
	if (arr_malloc == NULL)
	{
		perror("malloc fail!");
		return 1;
	}
	int  i = 0;
	for (i = 0; i < 10; i++)
	{
		arr_malloc[i] = i + 1;
	}
	int* ptr = (int*)realloc(arr_malloc, 80);
	if (ptr == NULL)
	{
		perror("realloc fail!");
		return 1;
	}
	arr_malloc = ptr;
	for (i = 10; i < 20; i++)
	{
		arr_malloc[i] = i + 1;
	}
	for (i = 0; i < 20; i++)
	{
		printf("%d ", arr_malloc[i]);
	}
	free(arr_malloc);
	arr_malloc = NULL;
	return 0;
}

可以看到ptr直接拷贝了原空间的内容,此时后面的空间大小足够,所以直接在原空间后开辟。,用完不要忘记free和置NULL

此时来看一下如果后面空间不够是不是真的重新开辟了呢?

如图所示,当原空间后的空间不够的时候确实会重新开辟,并把原空间的内容拷贝过去。

到这里动态开辟的函数我们讲解就完毕了,前面我已经发过了文件的,接下来我们来对我们上次的通讯录进行改进

枚举常量的简单说明及使用

枚举顾名思义就是一一列举的意思,

把可能的值列举出来

比如在生活中有星期一~星期天,可以一一列举

枚举和结构体的用法类似,不过枚举里面成员变量使用逗号隔开的。同时枚举里面如果你不规定的话,第一个默认为0,而且你枚举出来的值是可以直接使用的,不需要像结构体一样通过.或着->去访问成员。

通讯录2.0

通讯录1.0版本地址

1.0版本问题:

1.没办法对数据进行保存

2.空间大小固定了,不能修改
解决方案:

1.每次退出程序的时候把数据保存到文件中,初始化的时候导入数据到内存中

2.空间动态开辟,同时给一个容量,每次满了,就扩容。

同时这个的switch选项我们可以使用枚举美观一下

c 复制代码
enum
{
	EXIT,//0
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT//6
};

动态开辟通讯录,满了就扩容

此时我们应该把上面的data改成指针形式而不是数组,同时定义每次扩容扩充几个

c 复制代码
//定义容量
#define INC_SZ 3
//通讯录
typedef struct Contact
{
	int count;//表示当前人数
	PeoInfor* data;
	int capacity;

}Contact;

接下来就是改变初始化函数了。
原函数

改完之后

c 复制代码
//初始化通讯录
void InitialPeoInfor(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	pc->capacity = INC_SZ;//我这里把容量初始化为INC_SZ,写几个都可以看你自己
	pc->data = (PeoInfor*)malloc(pc->capacity * sizeof(PeoInfor));
	if (pc->data == NULL)
	{
		perror("malloc fail!");
		return;
	}
}

接下来就是扩容的问题了,扩容我们写在添加联系人这个函数里面

c 复制代码
//检查容量
void check_capacity(Contact* pc)
{
	//当我们通讯录的人数跟容量相同了,就说明满了,此时需要扩容
	if (pc->count == pc->capacity)
	{
		PeoInfor* ptr = (PeoInfor*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfor));
		if (ptr == NULL)
		{
			perror("realloc fail!");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;//扩容后把容量加上扩容的数量
		printf("扩容成功\n");
	}
}

此时运行可以发现当我们输完3个人再想输入的时候会提示扩容成功。

既然我们的通讯录是通过动态开辟来的,那么我们肯定要free掉,所以我们再写一个销毁数据的函数。

c 复制代码
//销毁数据
void DesTory(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
}

保存数据和载入数据

保存数据是当我们程序退出的时候保存,这里我们写一个SaveData()函数来实现保存数据

这里的数据我们以二进制的形式写入,这样就可以对数据进行一定的保护。

既然二进制的形式写入,我们来复习以下fwrite和fread这两个函数吧。

c 复制代码
//保存数据
void SaveData(Contact* pc)
{
	assert(pc);
	//                           wb表示以二进制形式写入
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen fail!");
		return;
	}
	int i = 0;
	for (i = 0; i < pc->count; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfor), 1, pf);
	}
	printf("保存成功\n");
}

同时载入数据的时候我们要对容量进行检查

c 复制代码
//载入数据
void LoadPeoInfor(Contact* pc)
{
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("Read data fopen");
		return;
	}
	PeoInfor tmp = { 0 };
	while (fread(&tmp, sizeof(PeoInfor), 1, pf) == 1)//每次读一个
	{
		check_capacity(pc);
		pc->data[pc->count] = tmp;
		pc->count++;
	}
}

//初始化通讯录
void InitialPeoInfor(Contact* pc)
{
	assert(pc);
	pc->count = 0;
	pc->capacity = INC_SZ;
	pc->data = (PeoInfor*)malloc(pc->capacity * sizeof(PeoInfor));
	if (pc->data == NULL)
	{
		perror("malloc fail!");
		return;
	}
	LoadPeoInfor(pc);
}

通讯录2.0演示

推荐好用的软件

这里推荐两款软件,一个就是我这个鼠标,他的图标变成了羽毛,还是挺方便找鼠标指针的。

第二个就是这个gif录制软件了,ScreenTOGif ,真的挺好用的。

安装包放在文章顶部啦~。

鼠标那个我上传上去了,好像只能上传一个,gif录制我直接给下载链接吧。
ScreenTOGif官网

相关推荐
奋斗的小花生8 分钟前
c++ 多态性
开发语言·c++
魔道不误砍柴功10 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
pianmian111 分钟前
python数据结构基础(7)
数据结构·算法
闲晨13 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程40 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法