结构体案例--简易通讯录的实现

通讯录的实现

  通讯录是我们最常见的一个应用,打开手机,找到通讯录,可以看出,一个联系人的信息包括最基本的姓名,电话。其他的我们有时候想保存一下联系人的QQ,地址以及性别等等。每个联系人都有这些基本信息(由于本人水平有限,所以本案例不考虑重名以及一个人有多个电话等复杂情况,只考虑最简单的情况),因此我们可以定义一个这样的结构体来存储这样的信息。假设我们要存放20个人的信息,那我我们可以进行如下声明:

#define MAX 20

#define MAX_NAME 20
#define MAX_TELE 12
#define MAX_ADDR 100
#define MAX_QQ 15
#define MAX_SEX 5
typedef struct PeoInfo
{
	char name[MAX_NAME];//姓名
	char tele[MAX_TELE];//电话
	char addr[MAX_ADDR];//地址
	char qq[MAX_QQ];//QQ
	char sex[MAX_SEX];//性别
	short age;//年龄
}PeoInfo;

  对于一个基本的通讯录,其应该包含的基本功能有增删查改排,即增加一个联系人,删除一个联系人,查找某个联系人,改正某个联系人的基本信息,排序通讯录(因为功能较为简陋,这里只按姓名进行排序),由于C本身并不能主动显示这些信息,所以我们还需添加一个显示的功能,最后退出程序。
  因此我们可以将这些功能封装起来,首先将这七个功能放在枚举列表里,让用户选择相应的数字来选择对应的功能。即

enum Option
{
	EXIT,//退出
	ADD,//增
	DEL,//删
	SEARCH,//查
	MODIFY,//改
	SORT,//排
	SHOW
};

  《扫雷小程序》以及《三子棋》项目中都对菜单有所描述,本篇文章将不再赘述,主要描述上述各个功能的实现(增删查改排的功能的实现)。
  我们考虑一个最坏的情况,当通讯录满的时候,则无法再向通讯录里添加信息,因此我们在向通讯录添加信息时,需要判断通讯录是否已满,则我们需要一个数,来记录通讯录联系人的个数。(同样的我们在删除,排序,以及显示联系人时同样的需要联系人个数)。因此一个通讯录包含两个信息:联系人的信息,联系人的个数,所以我们也可以用一个结构体来记录通讯录:

typedef struct Contact
{
	PeoInfo data[MAX];//用于记录所有联系人的信息
	int sz;//用于记录通讯录中联系人的个数,便于增删排序和显示
}Contact;

增-向通讯录添加一个联系人

  我们把联系人添加到了数组中,可以分析到,当data中没有联系人时,sz为0;此时添加联系人添加在data[0]处)(数组下标是从0开始的),此时sz要加1,变成了1,如果继续添加下一个联系人,此时联系人添加在data[1]处,可以看出,添加联系人时,被添加的联系人添加在位置data[sz]处,如前文所述,当通讯录已满时,则无法再次添加。由于不能及时看到联系人的情况,所以添加成功时应提示用户添加成功。所以代码实现如下:

void add_contact(Contact *pc)
{
	if((pc->sz)>= MAX)
	{
		printf("通讯录已满,无法添加。请删除部分联系人后再添加\n");
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s",pc->data[pc->sz].name);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		printf("请输入QQ:>");
		scanf("%s", pc->data[pc->sz].qq);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		pc->sz++;//添加一个联系人,sz要加1
		printf("添加成功。\n");
	}
	
}

删-从通讯录删除一个联系人

  删除联系人时,有两种情况无法删除,一是通讯录为空时,即sz为0,二是没有查到该联系人(这里仅查名字,又包括联系人名字输错和没有该联系人的情况,但终归是查不到)。所以删除前,我们还应实现一个查找函数,在找到该联系人后,返回该联系人所在的位置(即对应的数组下标),找不到就返回负数。
  查找代码实现如下:

static int find_peo(const Contact* pc)
{
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的名字:\n");
	scanf("%s", name);
	int i;
	for (i = 0; i < pc->sz;i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;//找到返回下标
		}
	}
	return -1;//找不到
}

  删除联系人代码:(删除时,只需要把该联系人后边的联系人(如果有的话)向前挪动一位,同时把sz减一)

//删除联系人
void del_contact(Contact* pc)
{
	if (pc->sz <= 0)
	{
		printf("通讯录为空,无法删除\n");
	}
	else
	{
		//查找
		int ret = find_peo(pc);
		if (ret == -1)
		{
			printf("未找到此人,请检查名字是否输入错误或确认该联系人是否在通讯录中\n");
		}
		//删除
		else
		{
			int j;
			for (j = ret; j < pc->sz-1; j++)
			{
				pc->data[j] = pc->data[j + 1];
			}
			pc->sz--;
			printf("删除成功\n");
		}
		
	}
}

查-从通讯录查找指定联系人

  在这里仅以名字查找,当找到对应联系人时,在屏幕输出对应的联系人的信息。代码实现如下:

//查找一个联系人
void search_contact(const Contact* pc)
{
	int ret = find_peo(pc);
	if (ret == -1)
	{
		printf("未找到此人,请检查名字是否输入错误或确认该联系人是否在通讯录中\n");
	}
	else
	{
		printf("%20s %5s %5s %15s %12s %50s\n",  "姓名", "性别", "年龄", "QQ", "电话", "地址");
		printf("%20s %5s %5d %15s %12s %50s\n",  pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].age,
			pc->data[ret].qq,
			pc->data[ret].tele,
			pc->data[ret].addr
		);
	}
}

改-更改指定联系人的信息

  同删除一样,我们首先得查找到这个联系人是否存在,如果存在应该在哪个位置,然后再去修改,其代码实现如下:

//修改指定联系人
void modify_contact(Contact* pc)
{
	int ret = find_peo(pc);//接收被查找的人所在的位置,如果找不到就返回-1.
	if (ret == -1)
	{
		printf("未找到此人,请检查名字是否输入错误或确认该联系人是否在通讯录中\n");
	}
	else
	{
		printf("请输入新的名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入新的电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入新的地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("请输入新的QQ:>");
		scanf("%s", pc->data[ret].qq);
		printf("请输入新的性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入新的年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("修改成功。\n");
	}
}

排-将通讯录里的联系人按指定规则排序

  为方便起见,这里按名字进行排序,排序方法使用冒泡排序,排序类型为升序。代码实现如下:

//按姓名排列
void sort_contact(Contact* pc)
{
	int i, j;
	int flag = 1;
	for (i = 0; (i < pc->sz - 1)&& flag; i++)
	{
		flag = 0;
		for (j = 0; j < pc->sz - 1 -  i; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				PeoInfo tmp = pc->data[j];
				pc->data[j] = pc->data[j+1];
				pc->data[j + 1] = tmp;
				flag = 1;
			}
		}
	}
}

显示-将通讯录中联系人的信息打印在屏幕上

  其实现方式和《扫雷小程序》《三子棋》中的棋盘打印类似。为了便于我们观察,我们在最前边加上表头,在第一列加上序号,其代码实现如下:

//显示信息
void show_contact(const Contact* pc)
{
	int i;
	printf("%5s %20s %5s %5s %15s %12s %50s\n","序号","姓名","性别","年龄","QQ","电话","地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%5d %20s %5s %5d %15s %12s %50s\n", i + 1, pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].qq,
			pc->data[i].tele,
			pc->data[i].addr
			);
	}
	printf("\n");
}

退出-退出该应用程序

  《扫雷小程序》《三子棋》退出程序方式一致。

总结

  最后,完整的文件如下《C语言结构体案例–简易通讯录的实现》
  由于水平有限,本案例仍存在许多致命缺陷,主要有以下几点:

  1. 退出程序后,通讯录随即销毁,重新进入程序得再创建;
  2. 联系人统一存放在数组中,数组的内存提前分配好,当我们超过20个联系人时就再也不能存入新的联系人,而当我们联系人不够时会有一部分空间一直空闲,造成浪费。
  3. 更改联系人信息时,必须要更改全部信息(而实际情况是我们往往只想更改其中某个信息);
  4. 每次只能删除一个联系人(无法批量删除);
  5. 排序方式单一。

  后续将针对上述问题,逐步做一些改进。
  本文完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值