【c语言进阶】动态通讯录

在这里插入图片描述

write in front
所属专栏: c语言学习
️博客主页:睿睿的博客主页
️代码仓库:VS2022_C语言仓库
您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!


文章目录

  • 前言:
  • 一、静态通讯录的弊端:
  • 二、动态增容优化通讯录:
    • 2.1.枚举类型的加入
    • 2.2.结构体的修改
    • 2.3.Initcontact函数的修改
    • 2.4.Addcontact函数的修改
    • 2.5.Desdrycontact函数的添加
  • 三、文件操作优化通讯录:
    • 3.1.Savecontact函数的添加(通讯录信息保存)
    • 3.2.Loadcontact函数的添加(导入通讯录信息)
  • 四、完整代码展示:
    • contact.h:
    • test.c
    • contact.c
  • 总结:

前言:

  在前面的学习中,我们写了静态通讯录:c语言实现静态通讯录

一、静态通讯录的弊端:

  对于简易版本的通讯录来说,我们是直接指定了通讯录的大小,那么此时我们会发现两个弊端:

弊端1:

  如果我们创建的通讯录空间过大,就会浪费空间;如果我们创建的通讯录太小又不够使用 。所以,此时我们就得优化通信录给定空间上的问题,不能再给其指定大小的空间,而是让其空间有灵活性此时我们以动态增容的方式来给定通讯录的空间,就很好的规避了这一问题!

弊端2:

  我们知道程序运行起来我们输入的数据都是保存在内存上的,在简易版本中我们在运行通讯录之后添加的数据,在程序结束的时候都会被清除。当而我们既然要保存联系人的信息,就得做到数据持久化的保存,我们就得将数据保存到硬盘中,也就是文件当中,此时我们可以用文件操作的方式来实现持久化的保存联系人信息。

二、动态增容优化通讯录:

2.1.枚举类型的加入

  在前面我们学习了枚举类型的使用,我们在写menu菜单的switch函数时,常常会因为不知道数字对应的是哪一个功能而弄混。
  枚举类型中各成员的从零开始依次递增,于是我们可以再使用一个枚举类型来使我们的选项对应用户的选择:

enum OPTION
{
	Exit = 0,
	Add = 1,
	Del,
	Search,
	Modify,
	Print,
	sort
};

  这样一来,我们就可以把case后面的1,2,3等数据换成对应的功能名字。
case 1:替换为case Add:

2.2.结构体的修改

  之前我们创建了两个结构体,第一个存放信息,第二个存放信息的数组和联系人数量,可是第二个存放的人信息的数组规定死了100个元素。所以我们将第二个结构体做一下改动即可:

//静态版本
//typedef struct contact
//{
//	PeoInfo data[MAX];
//	int sz;
//}contact;

//动态版本
typedef struct Contact
{
	PeoInfo* data;//指向存放人的信息的空间
	int sz;//当前已经放的信息的个数
	int capacity;//当前通讯录的最大容量
}contact;

  这里我们用sz记录联系人数量,用capacity指向当前通讯录的最大容量。在这里我们会发现结构体明显小了很多,因为存放人信息的空间我们还没有创建!

2.3.Initcontact函数的修改

  之前的初始化仅仅只是将所有的数据初始化为0,但是动态版本的通讯录还没有开辟联系人的空间。所有我们要在初始化函数里完成这个操作。

//静态版本:
//void Initcontact(contact* con)
//{
//	assert(con);
//	con->sz = 0;
//	memset(con->data, 0, sizeof(con->data));
//}

//动态版本:
void Initcontact(contact* con)
{
	assert(con);
	//DEFAULT_SZ是宏定义的3,在contact.h有添加!
	PeoInfo *ptr= (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	con->sz = 0;
	con->capacity = DEFAULT_SZ;
	con->data = ptr;

//这里的Loadcontact函数在下面会提到!
	LoadContact(con);
}

  在这里大家就不用自己初始化联系人信息了!因为calloc本身就有这个功能!

2.4.Addcontact函数的修改

  之前静态通讯录的添加联系人只需要在不超过容量的基础上添加即可,超过容量就不能操作了。但是动态通讯录在发现容量不足时要有自动增容的功能,这也是动态通讯录的有点所在!

//静态add
//void AddContact(contact* con)
//{
//	assert(con);
//	if (con->sz == 99)
//	{
//		printf("通讯录已经存满,请联系操作人员");
//		return;
//	}
//	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].tele);
//	printf("请输入联系人住址:>");
//	scanf("%s", con->data[con->sz].addr);
//	printf("联系人信息添加成功!\n");
//	con->sz++;
//}
// 
//判断容量是否够用!并且自动增容
void check_capacity(contact* con)
{
	assert(con);
	if (con->sz == con->capacity)
	{
		printf("内存已满,正在扩容——\n");
		Sleep(1000);
		PeoInfo* ptr = (PeoInfo*)realloc(con->data, (con->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr==NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		con->capacity += INC_SZ;
		con->data = ptr;
		printf("扩容成功\n");
	}
}
//动态add
void AddContact(contact* con)
{
	assert(con);
	check_capacity(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].tele);
	printf("请输入联系人住址:>");
	scanf("%s", con->data[con->sz].addr);
	printf("联系人信息添加成功!\n");
	con->sz++;
}

2.5.Desdrycontact函数的添加

  在使用完堆区的空间之后,最重要的事情是什么?当然是释放这块空间呀!

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

三、文件操作优化通讯录:

3.1.Savecontact函数的添加(通讯录信息保存)

  为了在保存内存中操作完成后的所有联系人信息,我们在程序退出之前将联系人信息以2进制的方式写进文件里面

void SaveContact(contact* con)
{
	FILE* fp = fopen("contact.txt", "wb");
	if (fp==NULL)
	{
		perror("SaveContact");
	}
	else
	{ 
		printf("正在保存\n");
		Sleep(1000);
		for (int i = 0; i < con->sz; i++)
			fwrite(con->data + i, sizeof(PeoInfo), 1, fp);
		fclose(fp);
		fp = NULL;
		printf("保存成功\n");
	}
}

3.2.Loadcontact函数的添加(导入通讯录信息)

  在我们对于通讯录进行修改时,要将之前文件中存好的信息导入内存中去,这个步骤也应该在初始化函数中进行,我们将他分装为函数,代码如下:

void LoadContact(contact* con)
{
	FILE* fp = fopen("contact.txt", "rb");
	PeoInfo str={ 0 };
	if (fp==NULL)
	{
		perror("LoadContact");
	}
	else
	{
		printf("正在加载>>>\n");
		Sleep(1000);
		int i = 0;
		while (fread(&str, sizeof(PeoInfo), 1, fp))
		{
			check_capacity(con);
			con->data[i] = str;
			con->sz++;
			i++;
		}
	}
	fclose(fp);
	fp = NULL;
	printf("加载成功!\n");
}

四、完整代码展示:

contact.h:


#pragma once
#include
#include
#include
#include

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 20

#define DEFAULT_SZ 3
#define INC_SZ 2

enum OPTION
{
	Exit = 0,
	Add = 1,
	Del,
	Search,
	Modify,
	Print,
	sort
};

typedef struct PeoInfo
{
	char name[MAX_NAME];
	char sex[MAX_SEX];
	int age;
	char addr[MAX_ADDR];
	char tele[MAX_TELE];
}PeoInfo;

//静态版本
//typedef struct contact
//{
//	PeoInfo data[MAX];
//	int sz;
//}contact;

//动态版本
typedef struct Contact
{
	PeoInfo* data;//指向存放人的信息的空间
	int sz;//当前已经放的信息的个数
	int capacity;//当前通讯录的最大容量
}contact;

//初始化消息
void Initcontact(contact *con);

//增加消息
void AddContact(contact *con);

//删除信息
void DelContact(contact *con);

//查找个别人消息
void SeachContact(contact *con);

//修改个别人消息
void ModifyContact(contact* con);

//打印全部通讯录
void PrintContact(contact* con);

//排序通讯录
void sortContact(contact* con);

//保存通讯录中的信息到文件中
void SaveContact(contact* con);

//加载文件信息到通讯录
void LoadContact(contact* con);

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"


void menu()
{
	printf("**********************************\n");
	printf("**********************************\n");
	printf("******** 欢迎使用本通讯录 ********\n");
	printf("**********************************\n");
	printf("***** 本通讯录现提供以下功能 *****\n");
	printf("************ 1.Add ***************\n");
	printf("************ 2.Del ***************\n");
	printf("************ 3.Search ************\n");
	printf("************ 4.Modify ************\n");
	printf("************ 5.Print *************\n");
	printf("************ 6.sort **************\n");
	printf("************ 0.exit**************\n");
	printf("**********************************\n");
}
void Contact()
{
	int input = 0;
	contact con;
	Initcontact(&con);
	do
	{
		menu();
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case Add:
			AddContact(&con);
			break;
		case Del:
			DelContact(&con);
			break;
		case Search:
			SeachContact(&con);
			break;
		case Modify:
			ModifyContact(&con);
			break;
		case Print:
			PrintContact(&con);
			break;
		case sort:
			sortContact(&con);
			break;
		case Exit:
			SaveContact(&con);
			DestroyContact(&con);
			printf("已成功退出");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);

}
int main()
{
	Contact();
	return 0;
}

contact.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contact.h"

int FindByName(contact* con, char name[])
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (strcmp(con->data[i].name,name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//静态版本:
//void Initcontact(contact* con)
//{
//	assert(con);
//	con->sz = 0;
//	memset(con->data, 0, sizeof(con->data));
//}

//动态版本:
void Initcontact(contact* con)
{
	assert(con);
	PeoInfo *ptr= (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	con->sz = 0;
	con->capacity = DEFAULT_SZ;
	con->data = ptr;

	LoadContact(con);
}

void DestroyContact(contact* con)
{
	free(con->data);
	con->data = NULL;
	con->sz = 0;
	con->capacity = 0;
	con = NULL;//??

}
//静态add
//void AddContact(contact* con)
//{
//	assert(con);
//	if (con->sz == 99)
//	{
//		printf("通讯录已经存满,请联系操作人员");
//		return;
//	}
//	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].tele);
//	printf("请输入联系人住址:>");
//	scanf("%s", con->data[con->sz].addr);
//	printf("联系人信息添加成功!\n");
//	con->sz++;
//}
// 

void check_capacity(contact* con)
{
	assert(con);
	if (con->sz == con->capacity)
	{
		printf("内存已满,正在扩容——\n");
		Sleep(1000);
		PeoInfo* ptr = (PeoInfo*)realloc(con->data, (con->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr==NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		con->capacity += INC_SZ;
		con->data = ptr;
		printf("扩容成功\n");
	}
}
//动态add
void AddContact(contact* con)
{
	assert(con);
	check_capacity(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].tele);
	printf("请输入联系人住址:>");
	scanf("%s", con->data[con->sz].addr);
	printf("联系人信息添加成功!\n");
	con->sz++;
}
void DelContact(contact* con)
{
	assert(con);
	char name[100];
	if (con->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	printf("请输入要删除人名字:>");
	scanf("%s", name);
	int ret = FindByName(con, name);
	int i = 0;
	if (-1 == ret)
	{
		printf("没有找到此人\n");
		return;
	}
	else
	{
		for (i = ret; i < con->sz-1; i++)
		{
			con->data[i] = con->data[i + 1];
		}
		con->sz--;
		memset(&con->data[i], 0, sizeof(con->data[i]));
		printf("删除成功!\n");
	}
}

void SeachContact(contact* con)
{
	assert(con);
	char name[MAX_NAME];
	if (con->sz == 0)
	{
		printf("通讯录为空,无法输入\n");
		return;
	}
	printf("请输入查找人名字");
	scanf("%s", name);
	int ret = FindByName(con, name);
	if (ret == -1)
	{
		printf("您查找的人不存在!\n");
		return;
	}
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "姓名", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", con->data[ret].name, 
		con->data[ret].age, 
		con->data[ret].sex, 
		con->data[ret].addr, 
		con->data[ret].tele);
}

void ModifyContact(contact* con)
{
	assert(con);
	char name[MAX_NAME] = { 0 };
	if (con->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	printf("请输入查找人名字");
	scanf("%s", name);
	int ret = FindByName(con, name);
	if (-1 == ret)
	{
		printf("您要修改的人不存在\n");
		return;
	}
	printf("请输入联系人姓名:>");
	scanf("%s", con->data[ret].name);
	printf("请输入联系人性别:>");
	scanf("%s", con->data[ret].sex);
	printf("请输入联系人年龄:>");
	scanf("%d", &(con->data[ret].age));
	printf("请输入联系人住址:>");
	scanf("%s", con->data[ret].addr);
	printf("请输入联系人联系方式:>");
	scanf("%s", con->data[ret].tele);
	printf("联系人信息修改成功!\n");
}

void PrintContact(contact* con)
{
	printf("%-10s\t%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n","联系人", "姓名", "年龄", "性别", "地址", "电话");
	for (int i = 0; i < con->sz; i++)
	{
		printf("%-10d\t%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n",i+1, con->data[i].name,
			con->data[i].age,
			con->data[i].sex,
			con->data[i].addr,
			con->data[i].tele);
	}
}

int cmp_con_by_name(const void* e1, const void* e2)
{
	return (strcmp(((PeoInfo *)e1)->name, ((PeoInfo*)e2)->name));
}
void sortContact(contact* con)
{
	assert(con);
	if (0 == con->sz)
	{
		printf("通讯录为空,无法排序\n");
		return;
	}
	qsort(con->data, con->sz, sizeof(con->data[0]), cmp_con_by_name);
	printf("排序成功\n");
}

void SaveContact(contact* con)
{
	FILE* fp = fopen("contact.txt", "wb");
	if (fp==NULL)
	{
		perror("SaveContact");
	}
	else
	{ 
		printf("正在保持\n");
		Sleep(1000);
		for (int i = 0; i < con->sz; i++)
			fwrite(con->data + i, sizeof(PeoInfo), 1, fp);
		fclose(fp);
		fp = NULL;
		printf("保存成功\n");
	}
}

void LoadContact(contact* con)
{
	FILE* fp = fopen("contact.txt", "rb");
	PeoInfo str={ 0 };
	if (fp==NULL)
	{
		perror("LoadContact");
	}
	else
	{
		printf("正在加载>>>\n");
		Sleep(1000);
		int i = 0;
		while (fread(&str, sizeof(PeoInfo), 1, fp))
		{
			check_capacity(con);
			con->data[i] = str;
			con->sz++;
			i++;
		}
	}
	fclose(fp);
	fp = NULL;
	printf("加载成功!\n");
}

总结:

  我们通过文件操作的知识和动态内存空间管理的知识终于完善了动态通讯录的编写!相信大家在自己真正写出来之后都会感觉学有所成!
  更新不易,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
c语言学习
算法
智力题
更新不易,辛苦各位小伙伴们动动小手,三连走一走 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

你可能感兴趣的:(c语言学习,c语言,java,c++)