动态实现通讯录
我们的手机都有通讯录的菜单,那么这是如何实现的呢?
@[TOC]
## 实现通讯录的总体思路
首先,要实现通讯录,就要考虑文件的封装的问题,我分成了3个文件,test.c文件是用来实现通讯录的整体框架的,contact.h作为头文件,用来定义局部变量和函数声明,而contact.c就是具体实现通讯录的功能的文件。
在明确了文件的问题以后,我们可以先将通讯录的整体框架搭建起来,将变量的定义和函数的声明写好后,就要进行通讯录最重要的功能开发的环节。
## 整体框架的搭建
首先我们使用 do while语句来打印出通讯录的菜单
```c
void menu()
{
printf("*************************************\n");
printf("*********1.add 2.del************\n");
printf("*********3.serach 4.modify*********\n");
printf("*********5.sort 6.print**********\n");
printf("*********0.exit *********\n");
printf("*************************************\n");
}
```
接着使用switch语句来实现功能的选择
那么具体应该有哪些功能呢?
> 1.增加人的信息
2.删除指定人的信息
3.查找人的信息
4.修改指定人的信息
5.排序通讯录的信息
6.定义出联系人的信息
0.退出通讯录
由于通讯录的功能较多,使用case+数字不利于我们将函数与case 语句对号入座,所以我们通过定义枚举来使进行区分
```c
enum Option //定义枚举常量通常是大写(因为是枚举常量)
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SORT,//5
PRINT,//6
};
```
## 标题局部变量的定义以及函数的声明
进行这一步,必须要思考清楚我们到底要记录联系人的哪些信息?
> 每个人的信息:
名字+年龄+性别+电话+地址
由于信息较多,可以定义一个结构体,又为了后期修改数据,所以可以使用宏定义来指定大小,由于之后的
功能函数会经常用到这个结构体,所以还可以使用typedef来将这个结构体的名称改的简单一些
```c
#define MAX_NAME 30//,宏定义,写成这样方便修改
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 1000
typedef struct PeoInfo//定义结构体类型(联系人的信息)
{
char name[MAX_NAME];//使用typedef来将struct PeoInfo改写为 PeoInfo
char sex[MAX_SEX];
int age;
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
```
我们还需要一个具体的通讯录结构体来实现数据的存放,考虑到通讯录是动态开辟内存的,所以此处我们规定
> 1.通讯录初始化之后,可以存放3个人的信息、
2.当空间存满时,就自动增加可以存放2个人信息的空间
也使用typedef来简化结构体名称
```c
在这里插入代码片typedef struct Contact
{
PeoInfo* data;//指向动态内存申请的空间,用来存放联系人的信息
int sz;//记录的是联系人的有效信息的个数
int capacity;//记录通讯录的最大容量
} Contact;
```
## 初始化通讯录
首先动态开辟可以存放3个联系人大小的空间,再考虑malloc会不会指向一个空指针,所以使用if语句进行判断,如果是空指针就打印错误的位置,并return结束
```c
void InitContact(Contact* pc)//这里的pc就是&con
{
pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->capacity = DEFAULT_SZ;//默认值3
pc->sz = 0;
}
```
## 实现增加函数
要实现动态开辟,首先要判断当通讯录中联系人的信息到达3时,就要使用realloc进行动态内存开辟,为了防止出现空指针的情况,需要进行判断
接下来只需要进行依次录***系人信息即可
```c
void AddContact(Contact* pc)
{
if (pc->sz == pc->capacity)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr != NULL)
{
pc->data = ptr;
pc->capacity += INC_SZ;
printf("增容成功\n");
return;
}
else
{
perror("AddContact");//打印错误地点
printf("增加联系人失败\n");
return;
}
}
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].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
```
由于后面要用到打印函数
所以在这里先写打印函数
## 实现打印函数
```c
```void PrintContact(const Contact* pc)
{
//打印标题
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
//打印信息 //加符号表示左对齐
int i = 0;
for (size_t i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age, //age是int类型的,要用%d
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
```
## 实现查找姓名函数
查找姓名函数有利于后面功能的简化
```c
static int FindName(Contact* pc, char name[])//加上static使其他文件用不了这个函数,起保护作用
{
int i = 0;{
for (size_t i = 0; i < pc->sz; i++)
if (strcmp(pc->data[i].name, name) == 0)
return i;//返回找到的联系人的下标
}
return -1;//找不到
}
## 实现删除函数
删除要分为两步:
1.查找有没有这个联系人
2.删除
```c
void DelConntact(Contact *pc)
{
char name[MAX_NAME]={0};
if (pc->sz == 0)
{
printf("通讯录为空,无需删除\n");
return;
}
//1.查找要删除的联系人(有/没有)
printf("请输入要删除的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc,name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
//2.有->删除
int i = 0;
for (size_t i = pos; i < pc->sz-1; i++)//-1防止越界
{
pc->data[i] = pc->data[i + 1];//覆盖掉要删除的信息
}
pc->sz--;//联系人的数目减少1
printf("删除成功\n");
}
```
## 实现查找函数
```c
void SearchContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
printf("请输入要查找的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
else//拷贝一下之前写的打印函数
{
//打印标题
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
//打印信息 //加符号表示左对齐
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
pc->data[pos].age, //age是int类型的,要用%d
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
```
## 实现修改函数
```c
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
printf("请输入要修改的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
else
{
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].tele);
printf("请输入地址:");
scanf("%s", pc->data[pos].addr);
pc->sz++;
printf("修改成功\n");
}
}
```
## 实现排序函数
要实现排序,可以使用qsort函数,此处提供两种类型排序
```c
//按姓名(由小到大)来排序
//int cmp(void*e1,void*e2)
//{
// return strcmp((char*)e1 , (char*)e2);
//}
//按年龄(由小到大)来排序
int cmp(void* e1, void* e2)
{
return ((PeoInfo*)e1)->age- ((PeoInfo*)e2)->age;
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), cmp);
}
```
## 使用free置空
当然,因为是创建了动态内存,所以最后要置空,销毁通讯录
本来数据就是存储在内存中的,又连接不上数据库,所以数据一定是会丢失的
所以最后还是要将空间还给操作系统,使用free
```c
//销毁通讯录--置空
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity=0;
}
```
这样子就完成了整个通讯录的实现,这里面确实有很多值得推敲的地方,也会有点遗憾,没能把数据保存下来,但是我相信随着自己知识的丰富,以后一定会写出更加漂亮的代码。如有不足之处,请大家指正,共同进步。
@[TOC]
## 实现通讯录的总体思路
首先,要实现通讯录,就要考虑文件的封装的问题,我分成了3个文件,test.c文件是用来实现通讯录的整体框架的,contact.h作为头文件,用来定义局部变量和函数声明,而contact.c就是具体实现通讯录的功能的文件。
在明确了文件的问题以后,我们可以先将通讯录的整体框架搭建起来,将变量的定义和函数的声明写好后,就要进行通讯录最重要的功能开发的环节。
## 整体框架的搭建
首先我们使用 do while语句来打印出通讯录的菜单
```c
void menu()
{
printf("*************************************\n");
printf("*********1.add 2.del************\n");
printf("*********3.serach 4.modify*********\n");
printf("*********5.sort 6.print**********\n");
printf("*********0.exit *********\n");
printf("*************************************\n");
}
```
接着使用switch语句来实现功能的选择
那么具体应该有哪些功能呢?
> 1.增加人的信息
2.删除指定人的信息
3.查找人的信息
4.修改指定人的信息
5.排序通讯录的信息
6.定义出联系人的信息
0.退出通讯录
由于通讯录的功能较多,使用case+数字不利于我们将函数与case 语句对号入座,所以我们通过定义枚举来使进行区分
```c
enum Option //定义枚举常量通常是大写(因为是枚举常量)
{
EXIT,//0
ADD,//1
DEL,//2
SEARCH,//3
MODIFY,//4
SORT,//5
PRINT,//6
};
```
## 标题局部变量的定义以及函数的声明
进行这一步,必须要思考清楚我们到底要记录联系人的哪些信息?
> 每个人的信息:
名字+年龄+性别+电话+地址
由于信息较多,可以定义一个结构体,又为了后期修改数据,所以可以使用宏定义来指定大小,由于之后的
功能函数会经常用到这个结构体,所以还可以使用typedef来将这个结构体的名称改的简单一些
```c
#define MAX_NAME 30//,宏定义,写成这样方便修改
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30
#define MAX 1000
typedef struct PeoInfo//定义结构体类型(联系人的信息)
{
char name[MAX_NAME];//使用typedef来将struct PeoInfo改写为 PeoInfo
char sex[MAX_SEX];
int age;
char tele[MAX_TELE];
char addr[MAX_ADDR];
}PeoInfo;
```
我们还需要一个具体的通讯录结构体来实现数据的存放,考虑到通讯录是动态开辟内存的,所以此处我们规定
> 1.通讯录初始化之后,可以存放3个人的信息、
2.当空间存满时,就自动增加可以存放2个人信息的空间
也使用typedef来简化结构体名称
```c
在这里插入代码片typedef struct Contact
{
PeoInfo* data;//指向动态内存申请的空间,用来存放联系人的信息
int sz;//记录的是联系人的有效信息的个数
int capacity;//记录通讯录的最大容量
} Contact;
```
## 初始化通讯录
首先动态开辟可以存放3个联系人大小的空间,再考虑malloc会不会指向一个空指针,所以使用if语句进行判断,如果是空指针就打印错误的位置,并return结束
```c
void InitContact(Contact* pc)//这里的pc就是&con
{
pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo));
if (pc->data == NULL)
{
perror("InitContact");
return;
}
pc->capacity = DEFAULT_SZ;//默认值3
pc->sz = 0;
}
```
## 实现增加函数
要实现动态开辟,首先要判断当通讯录中联系人的信息到达3时,就要使用realloc进行动态内存开辟,为了防止出现空指针的情况,需要进行判断
接下来只需要进行依次录***系人信息即可
```c
void AddContact(Contact* pc)
{
if (pc->sz == pc->capacity)
{
PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
if (ptr != NULL)
{
pc->data = ptr;
pc->capacity += INC_SZ;
printf("增容成功\n");
return;
}
else
{
perror("AddContact");//打印错误地点
printf("增加联系人失败\n");
return;
}
}
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].tele);
printf("请输入地址:");
scanf("%s", pc->data[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
```
由于后面要用到打印函数
所以在这里先写打印函数
## 实现打印函数
```c
```void PrintContact(const Contact* pc)
{
//打印标题
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
//打印信息 //加符号表示左对齐
int i = 0;
for (size_t i = 0; i < pc->sz; i++)
{
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name,
pc->data[i].age, //age是int类型的,要用%d
pc->data[i].sex,
pc->data[i].tele,
pc->data[i].addr);
}
}
```
## 实现查找姓名函数
查找姓名函数有利于后面功能的简化
```c
static int FindName(Contact* pc, char name[])//加上static使其他文件用不了这个函数,起保护作用
{
int i = 0;{
for (size_t i = 0; i < pc->sz; i++)
if (strcmp(pc->data[i].name, name) == 0)
return i;//返回找到的联系人的下标
}
return -1;//找不到
}
## 实现删除函数
删除要分为两步:
1.查找有没有这个联系人
2.删除
```c
void DelConntact(Contact *pc)
{
char name[MAX_NAME]={0};
if (pc->sz == 0)
{
printf("通讯录为空,无需删除\n");
return;
}
//1.查找要删除的联系人(有/没有)
printf("请输入要删除的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc,name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
//2.有->删除
int i = 0;
for (size_t i = pos; i < pc->sz-1; i++)//-1防止越界
{
pc->data[i] = pc->data[i + 1];//覆盖掉要删除的信息
}
pc->sz--;//联系人的数目减少1
printf("删除成功\n");
}
```
## 实现查找函数
```c
void SearchContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
printf("请输入要查找的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
else//拷贝一下之前写的打印函数
{
//打印标题
printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址");
//打印信息 //加符号表示左对齐
printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name,
pc->data[pos].age, //age是int类型的,要用%d
pc->data[pos].sex,
pc->data[pos].tele,
pc->data[pos].addr);
}
}
```
## 实现修改函数
```c
void ModifyContact(Contact* pc)
{
char name[MAX_NAME] = { 0 };
printf("请输入要修改的人的名字\n");
scanf("%s", &name);
int pos = FindName(pc, name);
if (pos == -1)
{
printf("查找不到该联系人\n");
return;
}
else
{
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].tele);
printf("请输入地址:");
scanf("%s", pc->data[pos].addr);
pc->sz++;
printf("修改成功\n");
}
}
```
## 实现排序函数
要实现排序,可以使用qsort函数,此处提供两种类型排序
```c
//按姓名(由小到大)来排序
//int cmp(void*e1,void*e2)
//{
// return strcmp((char*)e1 , (char*)e2);
//}
//按年龄(由小到大)来排序
int cmp(void* e1, void* e2)
{
return ((PeoInfo*)e1)->age- ((PeoInfo*)e2)->age;
}
void SortContact(Contact* pc)
{
qsort(pc->data, pc->sz, sizeof(struct PeoInfo), cmp);
}
```
## 使用free置空
当然,因为是创建了动态内存,所以最后要置空,销毁通讯录
本来数据就是存储在内存中的,又连接不上数据库,所以数据一定是会丢失的
所以最后还是要将空间还给操作系统,使用free
```c
//销毁通讯录--置空
void DestroyContact(Contact* pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity=0;
}
```
这样子就完成了整个通讯录的实现,这里面确实有很多值得推敲的地方,也会有点遗憾,没能把数据保存下来,但是我相信随着自己知识的丰富,以后一定会写出更加漂亮的代码。如有不足之处,请大家指正,共同进步。