通讯录的实现
通讯录是我们最常见的一个应用,打开手机,找到通讯录,可以看出,一个联系人的信息包括最基本的姓名,电话。其他的我们有时候想保存一下联系人的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语言结构体案例–简易通讯录的实现》。
由于水平有限,本案例仍存在许多致命缺陷,主要有以下几点:
- 退出程序后,通讯录随即销毁,重新进入程序得再创建;
- 联系人统一存放在数组中,数组的内存提前分配好,当我们超过20个联系人时就再也不能存入新的联系人,而当我们联系人不够时会有一部分空间一直空闲,造成浪费。
- 更改联系人信息时,必须要更改全部信息(而实际情况是我们往往只想更改其中某个信息);
- 每次只能删除一个联系人(无法批量删除);
- 排序方式单一。
后续将针对上述问题,逐步做一些改进。
本文完。