C语言实现动态管理通讯录信息系统(静态通讯录plus版)

前言:

1.0版本–>静态通讯录

  • 在上一个静态版本中,我们在使用的时候会发现有诸多不便,比如通讯录可以存放的信息总量是固定不变的,比如当我们打开通讯录写入数据后,保存再打开时上一次写入的数据消失不见了,本篇将详细讲解如何使用动态内存管理和文件操作解决这两个问题:

一.动态管理思想

1.通讯录结构体声明发生变化

  • 首先,在静态通讯录中,结构体的大小是固定好的,比如有20个空间,我们就没法存放30个人的信息,但是只存放3个人的信息那又太浪费了。
  • 所以,我们让通讯录结构体一开始只有3个空间,满了后就增容2个空间,这样可以提高空间利用率,需要用到动态内存管理:

原来1.0静态版本:


#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 10
#define ADDRESS_MAX 30
#define TELEPHONE_MAX 12
 
 
//定义一个结构体类型,这里存放人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char address[ADDRESS_MAX];
	char telephone[TELEPHONE_MAX];
}PeoInfo;
//所有人的都放到这里
typedef struct Contact
{
	PeoInfo data[MAX];
	int size;
}Contact;

现在2.0动态版本:

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12

#define DEFAULT_SZ 3
#define INC_SZ 2

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];  //姓名
	int age;              //年龄
	char sex[SEX_MAX];    //性别
	char addr[ADDR_MAX];  //住址
	char tele[TELE_MAX];  //电话号码
}PeoInfo;

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

解析:原来的data[MAX]数组存放信息,其大小MAX是固定的,现在通讯录随时会增容,故要改为指针指向每个人的信息,sz值随着通讯录中的人数动态增长:
在这里插入图片描述

2.通讯录结构体初始化发生变化

为每个结构体动态分配内存空间

//初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SIZE, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	pc->data = ptr;
	pc->size = 0;
	pc->capacity = DEFAULT_SIZE;
}

3.通讯录能够动态增容

对静态通讯录来说,满了就不能继续添加了。
我们为解决这一问题,给动态通讯录封装一个增容函数,以实现动态扩容:

//检查当前通讯录的容量:
//sz满了才增加容量,不满就没必要增加
void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//增加容量
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
		printf("增容成功\n");
	}
}

解析:只有当sz(即当前存在通信录中的人数)等于当前最大容量capaciity,即存满了的时候,才扩容,我们定义INC_SZ为每次再增加多少容量

4.通讯录销毁数据

由于现在的内存空间都是动态分配的,所以数据也都在堆区上,那么每次关闭通讯录的时候都要释放内存,也就是销毁通讯录,以防止内存泄漏:

//销毁通讯录
void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->size = 0;
	pc = NULL;
}

二.优化通讯录可持续读写信息

前面提到,静态版本中,当我们打开通讯录写入数据后,保存再打开时上一次写入的数据消失不见了,为解决这个问题,我们将每次输入到通讯录的信息保存的文件里,这样再打开程序的时候上次的数据仍然存在:

1.保存通讯录中的信息到文件中

//保存通讯录中的信息到文件中
void SaveContact(Contact* pc)
{
	//写数据
	//1, 打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//写数据
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
		}
		fclose(pf);
		pf = NULL;
		printf("保存成功\n");
	}
}

2.加载文件信息到通讯录中

再次打开通讯录需要从文件中读数据:

//加载文件信息到通讯录
void LoadContact(Contact* pc)
{
	//读数据
	//1. 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//2. 读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//需不需要增容
			check_capacity(pc);
			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		fclose(pf);
		pf = NULL;
	}
}

三.源码

1.text.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contatc.h"

void menu()
{
	printf("********************\n");
	printf("****1.add     2.del****\n");
	printf("****3.search  4.modify****\n");
	printf("****5.show    6.sort****\n");
	printf("****0.exit      ****\n");
	printf("********************\n");
}
enum Option
{
	EXIT,//0
	ADD, //1
	DEL, //2
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
int main()
{
	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:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			ShowContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("推出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

2.contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

//初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;

	//加载文件信息到通讯录
	LoadContact(pc);
}

//销毁通讯录
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	pc = NULL;
}


//检查当前通讯录的容量
void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//增加容量
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity::realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
		printf("增容成功\n");
	}
}


//增加联系人
void AddContact(Contact* pc)
{
	assert(pc);
	check_capacity(pc);

	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);

	pc->sz++;
}


//显示通讯录中的信息
void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].addr,
			pc->data[i].tele);
	}
}

//根据名字查找信息
int FindByName(const Contact* pc, char name[])
{
	assert(pc);

	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

//删除指定联系人
void DelContact(Contact* pc)
{
	assert(pc);

	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	//删除
	//找到要删除的人
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("要删除的人不存在\n");
		return;
	}

	int i = 0;
	//删除
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

//查找指定联系人
void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		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", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].addr,
		pc->data[pos].tele);
}

//修改指定联系人
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);

	printf("修改完成\n");
}

//保存通讯录中的信息到文件中
void SaveContact(Contact* pc)
{
	//写数据
	//1, 打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//写数据
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
		}
		fclose(pf);
		pf = NULL;
		printf("保存成功\n");
	}
}

//加载文件信息到通讯录
void LoadContact(Contact* pc)
{
	//读数据
	//1. 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//2. 读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//增容
			check_capacity(pc);
			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		fclose(pf);
		pf = NULL;
	}
}

3.contact.h

#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12

#define DEFAULT_SZ 3
#define INC_SZ 2

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];  //姓名
	int age;              //年龄
	char sex[SEX_MAX];    //性别
	char addr[ADDR_MAX];  //住址
	char tele[TELE_MAX];  //电话号码
}PeoInfo;

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

//初始化通讯录
void InitContact(Contact* pc);

//销毁通讯录
void DestroyContact(Contact* pc);

//增加联系人
void AddContact(Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//显示通讯录中的信息
void ShowContact(const Contact* pc);

//查找指定联系人
void SearchContact(const Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

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

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

本篇到此结束,码文不易,还请多多支持哦!!

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殿下p

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值