动态实现通讯录

我们的手机都有通讯录的菜单,那么这是如何实现的呢?

@[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;
}
```

这样子就完成了整个通讯录的实现,这里面确实有很多值得推敲的地方,也会有点遗憾,没能把数据保存下来,但是我相信随着自己知识的丰富,以后一定会写出更加漂亮的代码。如有不足之处,请大家指正,共同进步。





全部评论
嘿嘿,这个看着蛮有意思!
点赞 回复
分享
发布于 2022-08-22 09:58 江苏

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务