通讯录进阶版(实现动态内存管理和内容保存)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、动态内存管理
  • 二、内容保存和读取
  • 三、代码展示
  • 总结


前言

本篇文章主要为之前做的一个通讯录做一个补充和完善新增了动态管理和内容保存

之前通讯录的链接:http://t.csdn.cn/Xnk5J


一、动态内存管理

需求:实现动态内存管理,我们不必一开始就创建一个固定大小的数组,而是创建一个可以随着需求变化的动态内存空间,这样可以起到一个节省空间,且存量不受限制的作用

解决方案:我们主要应用了malloc函数进行开辟realloc函数进行扩容

1.在原先的初始化板块,用malloc函数开辟空间,初始空间为3

//初始化结构体数组
//静态版本
//void InitContact(struct contact* pc)
//{
//	pc->sz = 0;
//	memset((pc->contact), 0, MAX * sizeof(*pc->contact));//用memset函数将con中的数组的1000个值全部初始化为0
//}
//动态版本
void InitContact(struct contact* pc)
{
	pc->sz = 0;
	struct date* ptr=(struct date*) malloc(3*sizeof(struct date));//用calloc函数开辟三个大小的空间
	if (ptr != NULL)
	{
		
		pc->contact = ptr;
		pc->capacity = Init;
	}
	else
	{
		perror("InitContact:");
		return;
    }
}

2.在增加通讯录名单的那个功能上,进行一个扩容函数的判断,判断是否需要扩容,需要则扩容,不需要则继续

//增加
//静态版本
//void AddContact(struct contact* pc)
//{
//	printf("请输入名字:");
//	scanf("%s", pc->contact[pc->sz].name);//用sz来充当数组下标,避免在一个位置重复输入
//	printf("请输入性别:");
//	scanf("%s", pc->contact[pc->sz].sex);
//	printf("请输入年龄:");
//	scanf("%d", &pc->contact[pc->sz].age);
//	printf("请输入电话号:");
//	scanf("%s", pc->contact[pc->sz].tele);
//	printf("请输入地址:");
//	scanf("%s", pc->contact[pc->sz].add);
//	printf("输入完成\n");
//	pc->sz++;//每输入一个sz就计数一次
//}
//动态版本

int dilataion(struct contact* pc)//扩容函数
{
	if (pc->sz == pc->capacity)
	{
		struct date* ptr = (struct date*)realloc(pc->contact, ((pc->capacity) + 2) * sizeof(struct date));//用realloc函数扩容
		if (ptr != NULL)
		{
			pc->contact = ptr;
			pc->capacity += interval;
			printf("扩容成功\n");
			return 1;
		}
		else
		{
			perror("AddContact:");
			return 0;
		}
	}
	return 1;
}
void AddContact(struct contact* pc)
{
	int i = dilataion(pc);
	if (0 == i)//返回值为1则扩容成功或者无需扩容,返回值为0则是空间不足
	{
		return;
	}

	printf("请输入名字:");
	scanf("%s", pc->contact[pc->sz].name);//用sz来充当数组下标,避免在一个位置重复输入
	printf("请输入性别:");
	scanf("%s", pc->contact[pc->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &pc->contact[pc->sz].age);
	printf("请输入电话号:");
	scanf("%s", pc->contact[pc->sz].tele);
	printf("请输入地址:");
	scanf("%s", pc->contact[pc->sz].add);
	printf("输入完成\n");
	pc->sz++;//每输入一个sz就计数一次
}

二、内容保存和读取

需要:在退出时能够保存通讯录名单,进入时可以读取通讯录名单

解决方案:1.在退出功能的那一块,添加一个保存文件的功能,需要用到fopen函数打开,fwrite函数二进制写入数据,fclose函数关闭文件

//保存数据
void SaveContact(struct contact* pc)
{
	//打开文件
	FILE* ptr = fopen("data.txt", "wb");//以二进制写入的方式打开data文件
	if (NULL == ptr)
	{
		perror("fopen");//检查打开文件是否成功,不成功报错
		return;
	}
	//存入数据
	for (int i = 0; i < pc->sz; i++)
	{
		fwrite(pc->contact+i, sizeof(struct date), 1, ptr);
	}
	//关闭文件
	fclose(ptr);
	ptr = NULL;
}

2.在初始化内容后添加一个读取数据的功能,要用到fopen函数打开,fread函数二进制读取,fclose函数关闭

//读取数据
void ReadContact(struct contact* pc)
{
	//打开文件
	FILE* ptr = fopen("data.txt", "rb");//以二进制读取的方式打开文件
	if (NULL == ptr)
	{
		perror("fopen");
		return;
	}
	//读取文件
	struct date p = {0};
	while (fread(&p, sizeof(struct date), 1, ptr) >= 1)//读到数据则进入循环,没有读到则出循环
	{
		dilataion(pc);//判断是否需要扩容
		pc->contact[pc->sz] = p;
		pc->sz++;
	}

	//关闭文件
	fclose(ptr);
	ptr = NULL;
}

三、代码展示

扩容效果:

 保存效果:

 读取效果:

 

代码如下:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

int main()
{
	menu();
	int input;
	struct contact con;
	InitContact(&con);
	ReadContact(&con);
	do
	{
		printf("请选择:");
		scanf("%d", &input);
		switch(input)
		{
		  case 1:
			  AddContact(&con);
			  break;
		  case 2:
			  DelContact(&con);
			  break;
		  case 3:
			  SearchConstact(&con);
			  break;
		  case 4:
			  ModifyContact(&con);
			  break;
		  case 5:
			  ShowContact(&con);
			  break;
		  case 6:
			  SortContact(&con);
			  break;
		  case 0:
			  SaveContact(&con);
			  printf("保存成功");
			  freeContact(&con);
			  printf("退出通讯录");
              break;
		  default:
			  printf("选择错误,请重新选择");
			  break;
		}
	} while (input);
	return 0;
}

contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
//打印菜单
void menu()
{
	printf("**********************************\n");
	printf("*****   1.增加      2.删除   *****\n");
	printf("*****   3.查找      4.修改   *****\n");
	printf("*****   5.显示      6.排序   *****\n");
	printf("******        0.退出         *****\n");
	printf("**********************************\n");
}

//初始化结构体数组
//静态版本
//void InitContact(struct contact* pc)
//{
//	pc->sz = 0;
//	memset((pc->contact), 0, MAX * sizeof(*pc->contact));//用memset函数将con中的数组的1000个值全部初始化为0
//}
//动态版本
void InitContact(struct contact* pc)
{
	pc->sz = 0;
	struct date* ptr=(struct date*) malloc(3*sizeof(struct date));//用calloc函数开辟三个大小的空间
	if (ptr != NULL)
	{
		
		pc->contact = ptr;
		pc->capacity = Init;
	}
	else
	{
		perror("InitContact:");
		return;
    }
}
int dilataion(struct contact* pc);
//读取数据
void ReadContact(struct contact* pc)
{
	//打开文件
	FILE* ptr = fopen("data.txt", "rb");//以二进制读取的方式打开文件
	if (NULL == ptr)
	{
		perror("fopen");
		return;
	}
	//读取文件
	struct date p = {0};
	while (fread(&p, sizeof(struct date), 1, ptr) >= 1)//读到数据则进入循环,没有读到则出循环
	{
		dilataion(pc);//判断是否需要扩容
		pc->contact[pc->sz] = p;
		pc->sz++;
	}

	//关闭文件
	fclose(ptr);
	ptr = NULL;
}
//增加
//静态版本
//void AddContact(struct contact* pc)
//{
//	printf("请输入名字:");
//	scanf("%s", pc->contact[pc->sz].name);//用sz来充当数组下标,避免在一个位置重复输入
//	printf("请输入性别:");
//	scanf("%s", pc->contact[pc->sz].sex);
//	printf("请输入年龄:");
//	scanf("%d", &pc->contact[pc->sz].age);
//	printf("请输入电话号:");
//	scanf("%s", pc->contact[pc->sz].tele);
//	printf("请输入地址:");
//	scanf("%s", pc->contact[pc->sz].add);
//	printf("输入完成\n");
//	pc->sz++;//每输入一个sz就计数一次
//}
//动态版本

int dilataion(struct contact* pc)//扩容函数
{
	if (pc->sz == pc->capacity)
	{
		struct date* ptr = (struct date*)realloc(pc->contact, ((pc->capacity) + 2) * sizeof(struct date));//用realloc函数扩容
		if (ptr != NULL)
		{
			pc->contact = ptr;
			pc->capacity += interval;
			printf("扩容成功\n");
			return 1;
		}
		else
		{
			perror("AddContact:");
			return 0;
		}
	}
	return 1;
}
void AddContact(struct contact* pc)
{
	int i = dilataion(pc);
	if (0 == i)//返回值为1则扩容成功或者无需扩容,返回值为0则是空间不足
	{
		return;
	}

	printf("请输入名字:");
	scanf("%s", pc->contact[pc->sz].name);//用sz来充当数组下标,避免在一个位置重复输入
	printf("请输入性别:");
	scanf("%s", pc->contact[pc->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &pc->contact[pc->sz].age);
	printf("请输入电话号:");
	scanf("%s", pc->contact[pc->sz].tele);
	printf("请输入地址:");
	scanf("%s", pc->contact[pc->sz].add);
	printf("输入完成\n");
	pc->sz++;//每输入一个sz就计数一次
}
//查询函数
int FindContact(struct contact* pc, char name[])
{
	for (int i = 0; i < (pc->sz); i++)
	{
		if (0 == strcmp(pc->contact[i].name, name))
		{
			return i;//使用strcmp比较,相等即找到了,传回下标i
		}
	}
	return -1;//没找到则传回-1
}
//删除
void DelContact(struct contact* pc)
{
	char name[20];
	printf("请输入要删除的名字:");
	scanf("%s", name);
	int ret=FindContact(pc,name);//利用查找函数,查到传回下标,没有则传回-1
	if (ret == -1)
	{
		printf("没有所要删除的名字\n");
	}
	else
	{
		for (int i = ret; i < (pc->sz)-1; i++)
		{
			pc->contact[i] = pc->contact[i + 1];//删除的办法是:
			//将所要删除的那一组数据往后的数据整体向前挪移
		}
		pc->sz--;
	}
}
//查找
void SearchConstact(struct contact* pc)
{
	char name[20];
	printf("请输入要查找的名字:");
	scanf("%s", name);
	int ret = FindContact(pc, name);//利用查找函数,查到传回下标,没有则传回-1
	if (ret == -1)
	{
		printf("查无此人\n");
	}
	else//打印传回下标所对应的数据
	{
		printf("%-10s\t%-6s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "号码", "地址");
		printf("%-10s\t%-6s\t%-5d\t%-12s\t%-20s\n", pc->contact[ret].name,
			pc->contact[ret].sex,
			pc->contact[ret].age,
			pc->contact[ret].tele,
			pc->contact[ret].add);
	}
}
//修改
void ModifyContact(struct contact* pc)
{
	char name[20];
	printf("请输入要修改的名字:");
	scanf("%s", name);
	int ret = FindContact(pc, name);//利用查找函数,查到传回下标,没有则传回-1
	if (ret == -1)
	{
		printf("查无此人\n");
	}
	else//用传回的下标找到要修改的数据,进行修改
	{
		printf("请输入修改后的名字:");
		scanf("%s", pc->contact[ret].name);
		printf("请输入修改后的性别:");
		scanf("%s", pc->contact[ret].sex);
		printf("请输入修改后的年龄:");
		scanf("%d", &pc->contact[ret].age);
		printf("请输入修改后的电话号:");
		scanf("%s", pc->contact[ret].tele);
		printf("请输入修改后地址:");
		scanf("%s", pc->contact[ret].add);
		printf("修改完成\n");
	}
}
//显示
void ShowContact(struct contact* pc)
{
	printf("%-10s\t%-6s\t%-5s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "号码", "地址");
	for (int i = 0; i < (pc->sz); i++)
	{
		printf("%-10s\t%-6s\t%-5d\t%-12s\t%-20s\n", pc->contact[i].name,
			pc->contact[i].sex,
			pc->contact[i].age,
			pc->contact[i].tele,
			pc->contact[i].add);
	}
}
//排序
int sort(const void* e1, const void* e2)
{
	return strcmp(((struct date*)e1)->name, ((struct date*)e2)->name);//用strcmp函数比较相邻的名字的大小
}
void SortContact(struct contact* pc)
{
	qsort(pc->contact, pc->sz, sizeof(struct date), sort);//用qsort函数进行排序
}
//保存数据
void SaveContact(struct contact* pc)
{
	//打开文件
	FILE* ptr = fopen("data.txt", "wb");//以二进制写入的方式打开data文件
	if (NULL == ptr)
	{
		perror("fopen");//检查打开文件是否成功,不成功报错
		return;
	}
	//存入数据
	for (int i = 0; i < pc->sz; i++)
	{
		fwrite(pc->contact+i, sizeof(struct date), 1, ptr);
	}
	//关闭文件
	fclose(ptr);
	ptr = NULL;
}
//释放空间
void freeContact(struct contact* pc)
{
	free(pc->contact);
	pc->capacity = 0;
	pc->sz = 0;
}

contact.h



#include <stdio.h>
#include <string.h>//strcmp,memset所需要引用的头文件
#include <stdlib.h>

#define MAX 1000 //自定义数字取代固定数字,方便调整
#define my_name 20 //同上
#define my_sex 6 //同上
#define my_tele 12 //同上
#define my_add 20 //同上
#define Init 3;//初始大小
#define interval 2;//每次扩容的大小
//打印菜单
void menu();
//创建结构体
struct date
{
	char name[my_name];//名字
	char sex[my_sex];//性别
	int age;//年龄
	char tele[my_tele];//电话号码
	char add[my_add];//地址
};
//创建结构体数组,用来保存通讯录信息
// 静态版本
//struct contact
//{
//	struct date contact[MAX];//保存struct date类型数据的结构体数组
//	int sz;//用来计算存入了多少个信息
//};
//动态版本
struct contact
{
	struct date *contact;//创建指针用于动态内存扩容
	int sz;//用来计算存入了多少个信息
	int capacity;//统计容量
};
//初始化结构体数组
void InitContact(struct contact* pc);
//增加
void AddContact(struct contact* pc);
//删除
void DelContact(struct contact* pc);
//查找
void SearchConstact(struct contact* pc);
//修改
void ModifyContact(struct contact* pc);
//显示
void ShowContact(struct contact* pc);
//排序(按照名字排)
void SortContact(struct contact* pc);
//释放空间
void freeContact(struct contact* pc);
//保存数据
void SaveContact(struct contact* pc);
//读取数据
void ReadContact(struct contact* pc);

 


总结

以上就是今天的内容,希望铁子们可以有所收获

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值