【通讯录的实现】

雨淋湿了天空,毁的很讲究.......................................................................................................

目录

前言

一、【静态版的通讯录】

1.1【需求分析】

1.2【代码实现】

二、【动态版的通讯录】

2.1【需求分析】

2.2【代码实现】

三、【文件版本的通讯录】

3.1【需求分析】

3.2【代码实现】

总结


前言

编写通讯录的相关知识,敬请观看!


一、【静态版的通讯录】

1.1【需求分析】

实现一个能存放指定大小的通讯录,要求可以对其进行增删查改等操作,这里提供了分析,但是某些功能代码并没有展现。

1.2【代码实现】

这里运用了,模块化编程的思想,不了解的可以去看【简易版扫雷】【三子棋】,这里不再过多讲述。代码中也有关于思路的注释讲解,这里也不在赘述。

下面看代码

首先是头文件部分(声明模块):<contact.h>

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Max 100//实现通讯录大小可控
#define Max_name 20//实现存放姓名的空间可控
#define Max_sex 5//同上
#define Max_tele 12 //同上
#define Max_addr 20//同上
typedef struct PeoInfo//定义存放人员信息的结构体,并进行重命名为PeoInfo
{
	char name[Max_name];
	int age;
	char sex[Max_sex];
	char tele[Max_tele];
	char addr[Max_addr];
}PeoInfo;
typedef struct Contact
{
	PeoInfo data[Max];//PeoInfo data[100] //创建通讯录,存放信息
	int sz;          //int sz//用于记录通讯录中人员的实际个数信息
}Contact;
void Initcontact(Contact* pc);//通讯录的初始化
void AddContact(Contact* pc);//通讯录中,增加联系人
void Show(Contact* pc);//打印通讯录当中的信息
void DelContact(Contact* pc);//通讯录中,删除联系人
void SearchContact(Contact* pc);//通讯录中,查找联系人
void ModifyContact(Contact* pc);//通讯录中,修改联系人
void SortContact(Contact* pc);//通讯录中,给联系人进行排序

<contact.c>(功能模块)部分:

#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"
void Initcontact(Contact* pc)//对通讯录的信息进行初始化
{
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}
void AddContact(Contact* pc)
{
	if (pc->sz == 100)
	{
		printf("通讯录已满,添加失败\n");
		return;
	}
	printf("请输入要添加联系人的名字\n");
	//pc先找到,data数组,因为人员信息,都存放在data数组里,所以增加,人员信息要在
	//data数组里操作,我们可以想一下,当,data数组里实际人员信息为0时,sz也为0,此时
	//要添加新的人员信息,应该添加在哪里?
	//答案是添加到data数组里,下标为0的位置,这样说明数组添加了第一个人员信息,
	//同理,当数组里已经有8个人员信息时,sz==8,要添加第九个人员信息,应添加在
	//数组下标为8的位置,所以每次添加新的人员信息,都应添加到,数组元素下标为sz的位置。
	scanf("%s", (pc->data[pc->sz]).name);//由于sz定义在Contact里,要访问就需要通过pc指针来实现
	printf("请输入要添加联系人的年龄\n");
	scanf("%d", &((pc->data[pc->sz]).age));
	printf("请输入要添加联系人的性别\n");
	scanf("%s", ((pc->data[pc->sz]).sex));
	printf("请输入要添加联系人的电话\n");
	scanf("%s", ((pc->data[pc->sz]).tele));
	printf("请输入要添加联系人的地址\n");
	scanf("%s", ((pc->data[pc->sz]).addr));
	(pc->sz)++;
	printf("添加成功\n");
}
void Show(Contact* pc)
{
	//这样写,在信息的上方,增加了标签,更加美观。
	printf("%-8s %-8s %-8s %-12s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i < pc->sz; i++)
	{
		/*printf("名字是:%s\n", (pc->data[i]).name);
		printf("年龄是:%d\n", (pc->data[i]).age);
		printf("性别是:%s\n", (pc->data[i]).sex);
		printf("电话是:%s\n", (pc->data[i]).tele);
		printf("地址是:%s\n", (pc->data[i]).addr);*///这样写不是太美观
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[i]).name, (pc->data[i]).age, (pc->data[i]).sex, (pc->data[i]).tele, (pc->data[i]).addr);
    }
}
static int find_byname(Contact* pc, char name[])//加上static使得此函数,只能在当前.c文件中使用,别人无法使用。
{

	int pos1 = -1;//为pos1赋初始值-1,只要pos1没有被修改,说明该名字不存在于data数组当中。
	printf("请输入要查找联系人的姓名\n");
	scanf("%s", name);
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp((pc->data[i]).name, name) == 0)
		{
			return i;
		}
	}
	return pos1;
}
void DelContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	//删除之前,首先要找到该人员的信息,需要我们遍历data数组,并对数组存在该人员信息的位置进行记录。
	char name[Max_name] = { 0 };
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
        return;
	}
	//找到了,该人员信息后可以对其进行删除操作,做法就是从记录的位置开始,用后面的元素依次向前覆盖。
	//需要注意的是,当覆盖操作,进行到data数组里的最后一个元素时,此时最后一个元素不存在元素,因此只需要遍历到
	//倒数第二个元素即可。
	//1.这里是覆盖操作,也可以用memmove来实现。
	for (int j = pos; j < (pc->sz)-1; j++)
	{
		pc->data[j] = pc->data[j + 1];//依次向前覆盖。
	}
	(pc->sz)--;
	printf("删除成功\n");
	//2.这里用memmove进行实现,方法就是通过memmove对自身进行拷贝的功能,实现从pos+1的位置拷贝到pos的位置。
	/*memmove((pc->data) + pos, (pc->data) + pos + 1, ((pc->data)+(pc->sz)-(pc->data+pos))*sizeof(PeoInfo));
	(pc->sz)--;
	printf("删除成功\n");*/
}
enum WAY
{
	Stop,
	ByName,
	ByTele,
	Byaddr
};
void menu1()
{
	printf("*******************\n");
	printf("****1.  ByName ****\n");
	printf("****2.  ByTele ****\n");
	printf("****3.  Byaddr ****\n");
	printf("****0.  Stop   ****\n");
	printf("*******************\n");
}

void Find_byname(Contact* pc)
{
	char name[Max_name] = { 0 };
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	else
	{
		printf("该联系人信息如下\n");
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[pos]).name, (pc->data[pos]).age, (pc->data[pos]).sex, (pc->data[pos]).tele, (pc->data[pos]).addr);
	}
}
void SearchContact(Contact* pc)//这里查找的方法多样,比如,通过名字,电话,地址等,可以写成switch语句,实现不同的选择对应不同的方法
{
	int fway = 0;
	do
	{
		menu1();
		printf("请输入你想要查找的的方式\n");
		scanf("%d", &fway);
		switch (fway)
		{
		case ByName:
			Find_byname(pc);
			break;
		case ByTele://写出对应功能的函数即可,以下的同样
			break;
		case Byaddr:
			break;
		case Stop:
			break;
		}

	} while (fway);
}
void ModifyContact(Contact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	//修改之前,首先要找到该人员的信息,需要我们遍历data数组,并对数组存在该人员信息的位置进行记录。
	char name[Max_name] = { 0 };
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//这里为什么,修改就直接对他重新输入就可以完成呢?这是因为,scanf的功能,比如,我们在进行初始化的时候,其实也是先在数组里存放0,然后通过
	//scanf对数组里的内容进行替换。从而达到修改的效果。
	printf("请输入想要修改人员的信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", (pc->data[pos]).name);
	printf("请输入要修改联系人的年龄\n");
	scanf("%d", &((pc->data[pos]).age));
	printf("请输入要修改联系人的性别\n");
	scanf("%s", ((pc->data[pos]).sex));
	printf("请输入要修改联系人的电话\n");
	scanf("%s", ((pc->data[pos]).tele));
	printf("请输入要修改联系人的地址\n");
	scanf("%s", ((pc->data[pos]).addr));
	printf("修改成功\n");
}
void cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);//由于参与排序对象的类型为PeoInfo,所以这里也强转为PeoInfo类型
}
void SortContact(Contact* pc)//这里排序采用了qsort,其实,排序的方法也是多样的,我们可以通过回调函数,和函数指针,来实现复杂的功能。
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
	printf("排序成功\n");
}

<test.c>部分(测试模块):

#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"
void menu()//菜单,供用户,进行操作选择。
{
	printf("*******************\n");
	printf("****1.  ADD    ****\n");
	printf("****2.  DEL    ****\n");
	printf("****3.  SEARCH ****\n");
	printf("****4.  MODIFY ****\n");
	printf("****5.  SHOW   ****\n");
	printf("****6.  SORT   ****\n");
	printf("****0.  EXIT   ****\n");
	printf("*******************\n");
}
enum EU//定义枚举类型,提高代码可读性,让选择的内容更加直观。
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};

int main()
{
	int input = 0;
	Contact con;
	Initcontact(&con);//对通讯录进行初始化;
	
	/*printf("请输入你的选择:\n");
	scanf("%d", &input);*///每次完成一项操作,需要重新进行操作,所以写在do while循环里面
	do
	{
		menu();
		printf("请输入你的选择:\n");
		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:
			Show(&con);//打印当前通讯录中联系人存放情况
			break;
		case SORT://对联系人进行排序的函数
			SortContact(&con);
			break;
		case EXIT:
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
		}
	} while (input);//选0自动跳出循环
	return 0;
}

二、【动态版的通讯录】

2.1【需求分析】

上面的静态版本,把通讯录的大小固定死了为100,现在要实现一个动态开辟内存的版本,比如,通讯录最初只能存放3个人的信息,当通讯录存满以后,每次扩容,增加2个人的信息。

2.2【代码实现】

1.dcontact部分(声明模块)

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 EU//定义枚举类型,提高代码可读性,让选择的内容更加直观。
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
enum WAY//同上,同时能够使用户的选择更加直观。
{
	Stop,
	ByName,
	ByTele,
	Byaddr
};
typedef struct PeoInfo//定义存放人员信息的结构体,并进行重命名为PeoInfo
{
	char name[Max_name];
	int age;
	char sex[Max_sex];
	char tele[Max_tele];
	char addr[Max_addr];
}PeoInfo;
typedef struct Dcontact
{
	PeoInfo* data;//用指针以便于使用malloc来开辟空间,从而达到动态开辟内存的效果,data指向了存放数据的空间。
	int sz;//记录通讯录有效信息个数
	int capacity;//通讯录当前最大容量,比如这里初始时把capacity置为3个有效信息的空间,然后通讯录中的信息一旦超过3,就增加两个有效信息的空间
}Dcontact;
void Initcontact(Dcontact* pc);//通讯录的初始化
void AddDcontact(Dcontact* pc);//通讯录中,增加联系人
void Show(Dcontact* pc);//打印通讯录当中的信息
void DelDcontact(Dcontact* pc);//通讯录中,删除联系人
void SearchDcontact(Dcontact* pc);//通讯录中,查找联系人
void ModifyDcontact(Dcontact* pc);//通讯录中,修改联系人
void SortDcontact(Dcontact* pc);//通讯录中,给联系人进行排序
void DestroyDcontact(Dcontact* pc);//退出通讯录时,对通讯录开辟的空间进行释放。

2.dcontact部分(功能模块)

#define _CRT_SECURE_NO_WARNINGS
#include "dcontact.h"
void Initcontact(Dcontact* pc)//对通讯录的信息进行初始化
{
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));//首先为了存放人员信息,用malloc来开辟空间。
	if (pc == NULL)
	{
		printf("%s\n", strerror(errno));
		return;
	}//判断是否开辟成功
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;//容量置成初始值3
}
void DestroyDcontact(Dcontact* pc)
{
	free(pc->data);//对开辟的空间进行释放
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	printf("释放内存成功\n");
}
static int CheckCapacity(Dcontact* pc)//分装一个检查容量是否为满的函数。
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr=realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));//	当容量满时用realloc进行扩容操作。
		if (ptr == NULL)
		{
			printf("CheckCapacity:%s\n", strerror(errno));
			return 0;
		}//判断扩容是否成功。不成功返回0,成功返回1,同时如果不需要扩容也返回1.
		else
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功,当前容量:%d\n", pc->capacity);
			return 1;
		}
		return 1;
	}
}
void AddDcontact(Dcontact* pc)
{
	int ck=CheckCapacity(pc);
	if (ck == 0)
	{
		printf("扩容失败\n");//这里如果扩容失败,就不要在进行以下的输入操作了。
	}
	else
	{
		printf("请输入要添加联系人的名字\n");
		scanf("%s", (pc->data[pc->sz]).name);
		printf("请输入要添加联系人的年龄\n");
		scanf("%d", &((pc->data[pc->sz]).age));
		printf("请输入要添加联系人的性别\n");
		scanf("%s", ((pc->data[pc->sz]).sex));
		printf("请输入要添加联系人的电话\n");
		scanf("%s", ((pc->data[pc->sz]).tele));
		printf("请输入要添加联系人的地址\n");
		scanf("%s", ((pc->data[pc->sz]).addr));
		(pc->sz)++;
		printf("添加成功\n");
	}
}
void Show(Dcontact* pc)
{
	//这样写,在信息的上方,增加了标签,更加美观。
	printf("%-8s %-8s %-8s %-12s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i < pc->sz; i++)
	{
		/*printf("名字是:%s\n", (pc->data[i]).name);
		printf("年龄是:%d\n", (pc->data[i]).age);
		printf("性别是:%s\n", (pc->data[i]).sex);
		printf("电话是:%s\n", (pc->data[i]).tele);
		printf("地址是:%s\n", (pc->data[i]).addr);*///这样写不是太美观
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[i]).name, (pc->data[i]).age, (pc->data[i]).sex, (pc->data[i]).tele, (pc->data[i]).addr);
	}
}
static int find_byname(Dcontact* pc, char name[])//加上static使得此函数,只能在当前.c文件中使用,别人无法使用。
{

	int pas = -1;//为pas赋初始值-1,只要pas没有被修改,说明该名字不存在于data数组当中。
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp((pc->data[i]).name, name) == 0)
		{
			return i;
		}
	}
	return pas;
}
void DelDcontact(Dcontact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[Max_name] = { 0 };
	printf("请输入要删除的联系人的姓名\n");
	scanf("%s", name);
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	for (int j = pos; j < (pc->sz) - 1; j++)
	{
		pc->data[j] = pc->data[j + 1];
	}
	(pc->sz)--;
	printf("删除成功\n");
}

static void menu1()
{
	printf("*******************\n");
	printf("****1.  ByName ****\n");
	printf("****2.  ByTele ****\n");
	printf("****3.  Byaddr ****\n");
	printf("****0.  Stop   ****\n");
	printf("*******************\n");
}
void Find_byname(Dcontact* pc)
{
	char name[Max_name] = { 0 };
	printf("请输入要查找的联系人的姓名\n");
	scanf("%s", name);
	int pos=find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	else
	{
		printf("该联系人信息如下\n");
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[pos]).name, (pc->data[pos]).age, (pc->data[pos]).sex, (pc->data[pos]).tele, (pc->data[pos]).addr);
	}
}
void SearchDcontact(Dcontact* pc)//这里查找的方法多样,比如,通过名字,电话,地址等,可以写成switch语句,实现不同的选择对应不同的方法
{
	int fway = 0;
	do
	{
		menu1();
		printf("请输入你想要查找的的方式\n");
		scanf("%d", &fway);
		switch (fway)
		{
		case ByName:
			Find_byname(pc);
			break;
		case ByTele://写出对应功能的函数即可,以下的同样
			break;
		case Byaddr:
			break;
		case Stop:
			break;
		default:
			printf("选择错误,请重新选择\n");
		}

	} while (fway);
}
void ModifyDcontact(Dcontact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	//修改之前,首先要找到该人员的信息,需要我们遍历data数组,并对数组存在该人员信息的位置进行记录。
	char name[Max_name] = { 0 };
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//这里为什么,修改就直接对他重新输入就可以完成呢?这是因为,scanf的功能,比如,我们在进行初始化的时候,其实也是先在数组里存放0,然后通过
	//scanf对数组里的内容进行替换。从而达到修改的效果。
	printf("请输入想要修改人员的信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", (pc->data[pos]).name);
	printf("请输入要修改联系人的年龄\n");
	scanf("%d", &((pc->data[pos]).age));
	printf("请输入要修改联系人的性别\n");
	scanf("%s", ((pc->data[pos]).sex));
	printf("请输入要修改联系人的电话\n");
	scanf("%s", ((pc->data[pos]).tele));
	printf("请输入要修改联系人的地址\n");
	scanf("%s", ((pc->data[pos]).addr));
	printf("修改成功\n");
}
void cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);//由于参与排序对象的类型为PeoInfo,所以这里也强转为PeoInfo类型
}
void SortDcontact(Dcontact* pc)//这里排序采用了qsort,其实,排序的方法也是多样的,我们可以通过回调函数,和函数指针,来实现复杂的功能。
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
	printf("排序成功\n");
}

3.test部分(测试模块)

#define _CRT_SECURE_NO_WARNINGS
#include "dcontact.h"
void menu()//菜单,供用户,进行操作选择。
{
	printf("*******************\n");
	printf("****1.  ADD    ****\n");
	printf("****2.  DEL    ****\n");
	printf("****3.  SEARCH ****\n");
	printf("****4.  MODIFY ****\n");
	printf("****5.  SHOW   ****\n");
	printf("****6.  SORT   ****\n");
	printf("****0.  EXIT   ****\n");
	printf("*******************\n");
}
int main()
{
	int input = 0;
	Dcontact dcon;//创建通讯录其中包括,人员信息,通讯录当前最大容量,通讯录有效信息个数。
	Initcontact(&dcon);//对通讯录进行初始化;
	do
	{
		menu();
		printf("请输入你的选择\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddDcontact(&dcon);//添加联系人的函数实现
			break;
		case DEL:
			DelDcontact(&dcon);//删除联系人的函数实现
			break;
		case SEARCH:
			SearchDcontact(&dcon);//查询联系人的函数实现
			break;
		case MODIFY:
			ModifyDcontact(&dcon);//修改联系人信息的函数实现
			break;
		case SHOW:
			Show(&dcon);//打印当前通讯录中联系人存放情况
			break;
		case SORT://对联系人进行排序的函数
			SortDcontact(&dcon);
			break;
		case EXIT:
			DestroyDcontact(&dcon);//这里退出时要注意,由于存放通讯录的空间是malloc出来的,在不使用时应该提前对这块空间进行释放这里分装函数进行实现。
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
		}
    

	} while (input);
	return 0;
}
//这里只是简单版。

三、【文件版本的通讯录】

3.1【需求分析】

我们可以简单的发现,无论是上面的静态版本,还是动态版本,我们在使用过程中都会出现一个致命的问题。那就是数据不能长久的保存。比如我们这一次进入通讯录录入了5个人的信息,可是当我们退出通讯录再次进入程序时,会发现,上次录入的5个人的信息已经被自动删除了,既然是通讯录那么应该具备只有我们自身想要删除数据时通讯录才会删除数据,所以我们需要实现一个能够保存数据的通讯录,也就是我们的最终版本。

3.2【代码实现】

1.Fcontact.h部分(声明模块)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 EU//定义枚举类型,提高代码可读性,让选择的内容更加直观。
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
enum WAY//同上,同时能够使用户的选择更加直观。
{
	Stop,
	ByName,
	ByTele,
	Byaddr
};
typedef struct PeoInfo//定义存放人员信息的结构体,并进行重命名为PeoInfo
{
	char name[Max_name];
	int age;
	char sex[Max_sex];
	char tele[Max_tele];
	char addr[Max_addr];
}PeoInfo;
typedef struct Dcontact
{
	PeoInfo* data;//用指针以便于使用malloc来开辟空间,从而达到动态开辟内存的效果,data指向了存放数据的空间。
	int sz;//记录通讯录有效信息个数
	int capacity;//通讯录当前最大容量,比如这里初始时把capacity置为3个有效信息的空间,然后通讯录中的信息一旦超过3,就增加两个有效信息的空间
}Dcontact;
void Initcontact(Dcontact* pc);//通讯录的初始化
void AddDcontact(Dcontact* pc);//通讯录中,增加联系人
void Show(Dcontact* pc);//打印通讯录当中的信息
void DelDcontact(Dcontact* pc);//通讯录中,删除联系人
void SearchDcontact(Dcontact* pc);//通讯录中,查找联系人
void ModifyDcontact(Dcontact* pc);//通讯录中,修改联系人
void SortDcontact(Dcontact* pc);//通讯录中,给联系人进行排序
void DestroyDcontact(Dcontact* pc);//退出通讯录时,对通讯录开辟的空间进行释放。
void SaveFcontact(Dcontact* pc);//保存当前通讯录中联系人的信息
int CheckCapacity(Dcontact* pc);//检查通讯录的容量

2.Fcontact.c部分(功能模块)

内含注释说明:

#define _CRT_SECURE_NO_WARNINGS 1
#include "Fcontact.h"
void LoadFcontact(Dcontact* pc)
{
	//打开文件
	FILE* pf = fopen("fcontact.dat", "rb");//从fcontact.dat以二进制的形式读取文件
	if (pf == NULL)
	{
		perror("SaveFcontact::fopen:");
		return;
	}
	PeoInfo tmp = { 0 };//由于我们无法确定fcontact.dat中的数据有多少个,而且我们设置的通讯录初始容量为3,所以保险起见应该专门创建一个临时变量,
	                    //用来作为数据的临时存放点,如果通讯录中的容量没满,就把tmp的值存放到通讯录当中。
	while (fread(&tmp,sizeof(struct PeoInfo),1,pf))//这里fread的返回值是int类型,表示返回成功读取到的元素个数,也就是说当fcontact.dat里的数据被读取完后
		                                           //fread返回0,跳出循环。
	{
		CheckCapacity(pc);//每次把读取到的数据存放到通讯录之前,应该先检查一遍容量是否充足,充足则进行存放,不充足就进行扩容。
		pc->data[pc->sz] = tmp;//把读取到的数据存放到通讯录当中
		pc->sz++;//每存放一个,把通讯录的当前总数总数+1。
	}
	fclose(pf);
	pf = NULL;

}
void Initcontact(Dcontact* pc)//对通讯录的信息进行初始化
{
	pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));//首先为了存放人员信息,用malloc来开辟空间。
	if (pc == NULL)
	{
		printf("%s\n", strerror(errno));
		return;
	}//判断是否开辟成功
	pc->sz = 0;
	pc->capacity = DEFAULT_SZ;//容量置成初始值3
	//加载上保存的信息到这次的通讯录当中
	LoadFcontact(pc);//在初始化通讯录的时候,应该首先把上次录入的联系人信息先加载到通讯录当中。这里用函数来分装。
}
void DestroyDcontact(Dcontact* pc)
{
	free(pc->data);//对开辟的空间进行释放
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	printf("释放内存成功\n");
}
static int CheckCapacity(Dcontact* pc)//分装一个检查容量是否为满的函数。
{
	if (pc->sz == pc->capacity)
	{
		PeoInfo* ptr = realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));//	当容量满时用realloc进行扩容操作。
		if (ptr == NULL)
		{
			printf("CheckCapacity:%s\n", strerror(errno));
			return 0;
		}//判断扩容是否成功。不成功返回0,成功返回1,同时如果不需要扩容也返回1.
		else
		{
			pc->data = ptr;
			pc->capacity += INC_SZ;
			printf("增容成功,当前容量:%d\n", pc->capacity);
			return 1;
		}
		return 1;
	}
}
void AddDcontact(Dcontact* pc)
{
	int ck = CheckCapacity(pc);
	if (ck == 0)
	{
		printf("扩容失败\n");//这里如果扩容失败,就不要在进行以下的输入操作了。
	}
	else
	{
		printf("请输入要添加联系人的名字\n");
		scanf("%s", (pc->data[pc->sz]).name);
		printf("请输入要添加联系人的年龄\n");
		scanf("%d", &((pc->data[pc->sz]).age));
		printf("请输入要添加联系人的性别\n");
		scanf("%s", ((pc->data[pc->sz]).sex));
		printf("请输入要添加联系人的电话\n");
		scanf("%s", ((pc->data[pc->sz]).tele));
		printf("请输入要添加联系人的地址\n");
		scanf("%s", ((pc->data[pc->sz]).addr));
		(pc->sz)++;
		printf("添加成功\n");
	}
}
void Show(Dcontact* pc)
{
	//这样写,在信息的上方,增加了标签,更加美观。
	printf("%-8s %-8s %-8s %-12s %-20s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i < pc->sz; i++)
	{
		/*printf("名字是:%s\n", (pc->data[i]).name);
		printf("年龄是:%d\n", (pc->data[i]).age);
		printf("性别是:%s\n", (pc->data[i]).sex);
		printf("电话是:%s\n", (pc->data[i]).tele);
		printf("地址是:%s\n", (pc->data[i]).addr);*///这样写不是太美观
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[i]).name, (pc->data[i]).age, (pc->data[i]).sex, (pc->data[i]).tele, (pc->data[i]).addr);
	}
}
static int find_byname(Dcontact* pc, char name[])//加上static使得此函数,只能在当前.c文件中使用,别人无法使用。
{

	int pas = -1;//为pas赋初始值-1,只要pas没有被修改,说明该名字不存在于data数组当中。
	for (int i = 0; i < pc->sz; i++)
	{
		if (strcmp((pc->data[i]).name, name) == 0)
		{
			return i;
		}
	}
	return pas;
}
void DelDcontact(Dcontact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	char name[Max_name] = { 0 };
	printf("请输入要删除的联系人的姓名\n");
	scanf("%s", name);
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	for (int j = pos; j < (pc->sz) - 1; j++)
	{
		pc->data[j] = pc->data[j + 1];
	}
	(pc->sz)--;
	printf("删除成功\n");
}

static void menu1()
{
	printf("*******************\n");
	printf("****1.  ByName ****\n");
	printf("****2.  ByTele ****\n");
	printf("****3.  Byaddr ****\n");
	printf("****0.  Stop   ****\n");
	printf("*******************\n");
}
void Find_byname(Dcontact* pc)
{
	char name[Max_name] = { 0 };
	printf("请输入要查找的联系人的姓名\n");
	scanf("%s", name);
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	else
	{
		printf("该联系人信息如下\n");
		printf("%-8s %-8d %-8s %-12s %-20s\n",
			(pc->data[pos]).name, (pc->data[pos]).age, (pc->data[pos]).sex, (pc->data[pos]).tele, (pc->data[pos]).addr);
	}
}
void SearchDcontact(Dcontact* pc)//这里查找的方法多样,比如,通过名字,电话,地址等,可以写成switch语句,实现不同的选择对应不同的方法
{
	int fway = 0;
	do
	{
		menu1();
		printf("请输入你想要查找的的方式\n");
		scanf("%d", &fway);
		switch (fway)
		{
		case ByName:
			Find_byname(pc);
			break;
		case ByTele://写出对应功能的函数即可,以下的同样
			break;
		case Byaddr:
			break;
		case Stop:
			break;
		default:
			printf("选择错误,请重新选择\n");
		}

	} while (fway);
}
void ModifyDcontact(Dcontact* pc)
{
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法修改\n");
		return;
	}
	//修改之前,首先要找到该人员的信息,需要我们遍历data数组,并对数组存在该人员信息的位置进行记录。
	char name[Max_name] = { 0 };
	int pos = find_byname(pc, name);
	if (pos == -1)
	{
		printf("没有该联系人\n");
		return;
	}
	//这里为什么,修改就直接对他重新输入就可以完成呢?这是因为,scanf的功能,比如,我们在进行初始化的时候,其实也是先在数组里存放0,然后通过
	//scanf对数组里的内容进行替换。从而达到修改的效果。
	printf("请输入想要修改人员的信息\n");
	printf("请输入要修改联系人的名字\n");
	scanf("%s", (pc->data[pos]).name);
	printf("请输入要修改联系人的年龄\n");
	scanf("%d", &((pc->data[pos]).age));
	printf("请输入要修改联系人的性别\n");
	scanf("%s", ((pc->data[pos]).sex));
	printf("请输入要修改联系人的电话\n");
	scanf("%s", ((pc->data[pos]).tele));
	printf("请输入要修改联系人的地址\n");
	scanf("%s", ((pc->data[pos]).addr));
	printf("修改成功\n");
}
void cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);//由于参与排序对象的类型为PeoInfo,所以这里也强转为PeoInfo类型
}
void SortDcontact(Dcontact* pc)//这里排序采用了qsort,其实,排序的方法也是多样的,我们可以通过回调函数,和函数指针,来实现复杂的功能。
{
	qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);
	printf("排序成功\n");
}
void SaveFcontact(Dcontact* pc)
{
	//打开文件
	FILE* pf = fopen("fcontact.dat", "wb");//以二进制的形式打开fcontact.dat文件并写入。
	if (pf == NULL)//判断是否打开成功。
	{
		perror("SaveFcontact::fopen:");
		return;
	}
	//写入数据
	for (int i = 0; i < pc->sz; i++)//循环遍历通讯录中的各个信息
	{
		fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf);//把通讯录中的信息写入到fcontact.dat,一次写一个大小为PeoInfo的数据//
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
	printf("保存成功...\n");
}

3.test.c部分(测试模块)

#define _CRT_SECURE_NO_WARNINGS
#include "Fcontact.h"
void menu()//菜单,供用户,进行操作选择。
{
	printf("*******************\n");
	printf("****1.  ADD    ****\n");
	printf("****2.  DEL    ****\n");
	printf("****3.  SEARCH ****\n");
	printf("****4.  MODIFY ****\n");
	printf("****5.  SHOW   ****\n");
	printf("****6.  SORT   ****\n");
	printf("****0.  EXIT   ****\n");
	printf("*******************\n");
}
int main()
{
	int input = 0;
	Dcontact dcon;//创建通讯录其中包括,人员信息,通讯录当前最大容量,通讯录有效信息个数。
	Initcontact(&dcon);//对通讯录进行初始化;
	do
	{
		menu();
		printf("请输入你的选择\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddDcontact(&dcon);//添加联系人的函数实现
			break;
		case DEL:
			DelDcontact(&dcon);//删除联系人的函数实现
			break;
		case SEARCH:
			SearchDcontact(&dcon);//查询联系人的函数实现
			break;
		case MODIFY:
			ModifyDcontact(&dcon);//修改联系人信息的函数实现
			break;
		case SHOW:
			Show(&dcon);//打印当前通讯录中联系人存放情况
			break;
		case SORT://对联系人进行排序的函数
			SortDcontact(&dcon);
			break;
		case EXIT:
			SaveFcontact(&dcon);//每次退出通讯录时,应首先把本次在通讯录当中录入的人员信息进行保存,以便以后使用时查看。
			DestroyDcontact(&dcon);//这里退出时要注意,由于存放通讯录的空间是malloc出来的,在不使用时应该提前对这块空间进行释放这里分装函数进行实现。
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
		}


	} while (input);
	return 0;
}


总结

本篇博客实现了2种版本的通讯录这里说明一下,两种版本的通讯录并不完善,只是做到了简单的增删查改,还有很多功能可以添加,但是适合初学者进行学习,到这里就结束了,感谢观看!


.............................................................................要怎么证明我没有力气,我只有一天的回忆

                                                                                              ————《给我一首歌的时间》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值