通讯录:静态和动态

发布于:2022-12-07 ⋅ 阅读:(756) ⋅ 点赞:(0)

通讯录功能预览 :         

      0. 退出通讯录                  

  1. 添加联系人信息
  2. 删除指定联系人信息
  3. 查找指定联系人信息
  4. 修改指定联系人信息
  5. 显示所有联系人信息
  6. 以年龄大小排序所有联系人
  7. 清空所有联系人

为了增加代码的灵活性,定义了一些标识符常量

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 20
#define TELE_MAX 20
#define ADDR_MAX 20
#define capacity_max 3
#define increase_sz 2

结构体声明:

1.联系人结构体,通过姓名,性别,年龄,电话,住址来描述一个联系人。

struct PeoInfo
{
    char name[NAME_MAX];
    char sex[SEX_MAX];
    int age;
    char tele[TELE_MAX];
    char addr[ADDR_MAX];
};

2.通讯录结构体,这个结构体的成员变量应该包括一个存储联系人的数组(或者指向存储联系人空间的指针)和一个记录已存储多少联系人的变量。下面是静态和动态的两种不同定义方法。

静态版本:固定可存储联系人的大小

//静态版本
struct Contact
{
	struct PeoInfo date[MAX];
	int sz;
};

动态版本:除了记录存储联系人个数的变量sz外,还增加了一个记录通讯的录容量的变量capacity。定义了一个指向存储联系人空间的结构体指针

struct Contact
{
	struct PeoInfo* data;//指向了存储数据的空间
	int capacity;//通讯录的容量
	int sz;//已经存储联系人的个数
};

通讯录实现的测试逻辑比较简单,所以本篇文章重点讨论用静态和动态不同方法来实现通讯录的功能。逻辑测试在文章最后的test.c中,这里不重点说明。

通讯录初始化:

静态版本:通过memset函数初识化数组中的各个元素。这个函数在之前介绍过,三个参数分别为1.指向要填充的内存块的指针,2.要设置的值,3.改的大小。

void InitContact(struct contact* pc)
{
	//断言
	assert(pc != NULL);
	//初始化
	memset(pc->date,0,MAX*sizeof(struct people));
	pc->sz = 0;
}

动态版本:这里的重点是使用malloc()动态开辟了3个存储联系人的空间。

//通讯录初始化
void InitContact(struct Contact* pc)
{
	//断言
	assert(pc != NULL);
	//动态内存开辟
	pc->data = (struct PeoInfo*)malloc(capacity_max*sizeof(struct PeoInfo));
	//判断
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->capacity = capacity_max;
	pc->sz = 0;
}

 初始化完成以后,前期的准备工作就完成的差不多了,接下来就是各个通讯录功能的实现。

添加联系人:

静态版本:

这个版本的逻辑相对来说简单一些,只需要判断sz的个数是否在最大存储联系人个数MAX的范围内就可以了。如果sz<MAX,输入联系人的信息,sz的个数++。

//增加联系人
void Contact_add(struct contact* pc)
{
	//断言
	assert(pc != NULL);
	//判断是否还可以添加
	if (pc->sz == MAX)
	{
		printf("联系人已满,无法添加");
	}
	printf("请输入联系人姓名:>");
	scanf("%s",pc->date[pc->sz].name);
	printf("请输入联系人性别:>");
	scanf("%s", pc->date[pc->sz].sex);
	printf("请输入联系人年龄:>");
	scanf("%d", &pc->date[pc->sz].age);
	printf("请输入联系人电话:>");
	scanf("%s", pc->date[pc->sz].tele);
	printf("请输入联系人地址:>");
	scanf("%s", pc->date[pc->sz].addr);
	pc->sz++;
	printf("联系人添加成功\n");
}

动态版本:

在这个版本中,我们初始化的容量只够存储3个联系人,这个时候就需要判断一下,如果当sz=capacity的时候,就需要增加存储联系人的容量(可以使用realloc函数),为了逻辑的清晰,把这个过程封装成一个函数。

这个函数只需要在contact.c内部使用,可以用static修饰一下。简单的说明:在不需要增容和增容成功的情况下返回1。如果增容打印错误信息,返回0。

//判断是否需要增容
static int check_capacity(struct Contact* pc)
{
	//联系人的存储数量和容量相等
	if (pc->sz == pc->capacity)
	{
		//增容
		struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data,pc->capacity+increase_sz *sizeof(struct PeoInfo));
		//判断
		if (ptr == NULL)
		{
			perror("check_capacity");
			return 0;
		}
		else
		{
			//增容成功
			pc->data = ptr;
			pc->capacity = pc->capacity + increase_sz;
			printf("增容成功!\n");
			return 1;
		}
	}
	else
	{
		return 1;
	}

}

动态版本增加联系人:如果check_capacity()的返回值是1,代表不需要增容或者增容成功,录入联系人的消息即可,sz的个数++。

//动态版本添加联系人
void AddContact(struct Contact* pc)
{
	//断言
	assert(pc);
	if (1 == check_capacity(pc))
	{
		//增加人的信息
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
	else
	{
		printf("增容出现错误!\n");
	}
}

两个版本的添加联系人功能就分别完成了。 这里说明一下,因为这两个版本的通讯录是分开来写的,所以在函数名和变量名的部分可能略有差异。

编写好第一个功能后,最好是打印出来看下效果,这样也可以及时的发现错误。

两个版本的打印是一样的。

打印联系人:

//打印通讯录
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", 
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr);
	}
}

删除联系人:

要完成这个功能,第一步就是找到这个联系人。后面的查询,修改,也要经历这个过程,可以把找到指定联系人这个动作封装成一个函数。

//查找联系人
static int FindByName(const struct Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;
}

通过strcmp这个函数比较两个字符串是否相同,相同返回这个联系人的下标。

删除:两个版本的删除没有什么区别。

//删除联系人
void DelContact(struct Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要删除人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人不存在");
	}
	else
	{
		//删除该联系人
		for(int j = ret;j<pc->sz-1;j++)
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("该联系人已删除,不在联系!\n");
	}
}

接下来的功能两个版本的实现是一样的。 

查询:

//查询联系人
SearchContact(const struct Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要查询的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人不存在\n");
	}
	else
	{
		printf("找到了\n");
		printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址");
		printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n",
			pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].age,
			pc->data[ret].tele,
			pc->data[ret].addr);
	}
}

 修改: 

//修改联系人信息
void ModifyContact(struct Contact* pc)
{
	char name[NAME_MAX];
	printf("请输入要修改联系人名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
	{
		printf("该联系人不存在\n");
	}
	else
	{
		//修改人的信息
		printf("请输入更改后名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入更改后的性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入更改后的年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("请输入更改后的电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入更改后的地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("修改成功!\n");
	}
}

 排序:这里我们选择使用qsort()这个库函数根据年龄从小到大进行排序。qsort这个函数在之前的文章中进行过模拟实现。

int CmpAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄排序
void SortContact(const struct Contact* pc)
{
	qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAge);
	printf("排序成功!\n");
}

清空联系人:

//清空联系人
void EmptyContact(struct Contact* pc)
{
	InitContact(pc);
	printf("通讯录已经清空\n");
}

退出: 动态版本在退出通讯录时,要记得释放内存空间。

//释放空间
void DestroyContact(struct Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

效果截图:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contacts.h"

void menu()
{
	printf("**************************************\n");
	printf("*****   1. add      2. del       *****\n");
	printf("*****   3. search   4. modify    *****\n");
	printf("*****   5. show     6. sort      *****\n");
	printf("*****   7. empty    0. exit      *****\n");
	printf("**************************************\n");
}

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	EMPTY
};


int main()
{
	//创建动态的通讯录
	struct Contact con;
	//通讯录初始化
	InitContact(&con);
	int input = 0;
	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:
			SortContact(&con);
			break;
		case EMPTY:
			EmptyContact(&con);
			break;
		case EXIT:
			DestroyContact(&con);
			printf("退出通讯录\n");
			break;
		default:
			printf("选项错误,请重新输入:>\n");
			break;
		}
	} while (input);
	return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到