全文目录
- 😀 前言
- 🤔 模块和功能划分
- 🤨 数据类型的选择
- 😮 功能序号类型 `enum`
- 😮 个人信息类型 `PeoInfo`
- 😮 通讯录类型 `Contact`
- 😵💫 功能的实现
- 🙄 初始化通讯录 `InitContact`
- 🙄 添加功能 `AddContact`
- 🙄 打印功能 `PrintContact`
- 🙄 删除联系人 `DeleteContact`
- 😍 通过名字查找对应下标 `FindByName`
- 🙄 查找联系人 `SearchContact`
- 🙄 修改联系人信息 `ModifyContact`
- 🙄 清空通讯录 `EmptyContact`
- 🙄 排序功能 `SortContact`
- 🌈 总结
😀 前言
前面我们学习了C语言的结构体等自定义类型的知识,乌泱泱的一大堆知识,没有练习肯定是学不扎实的,所以今天我们将前面学到的所有C语言知识整合起来进行实现一个小项目:通讯录(静态数组简易版),达到一个复习的目的。
🤔 模块和功能划分
我们实现的通讯录主要有以下几个功能:
- 退出
(exit)
- 添加
(add)
- 删除
(delete)
- 查找
(search)
- 修改
(modify)
- 打印
(print)
- 清空
(empty)
- 排序
(sort)
同时为了方便调试和代码的美观,我们将代码分为三个源文件:test.c, contact.h, contact.c
🤨 数据类型的选择
😮 功能序号类型 enum
对于各种功能,在实现的时候是通过switch case
语句来实现各种功能的:
do {menu();printf("请选择->");scanf("%d", &input);switch (input) {case 1: // 添加...break;case 2: // 删除...break;case 3: // 查找...break;case 4: // 修改...break;case 5: // 打印...break;case 6: // 清空...break;case 7: // 排序...break;case 0: // 退出...break;default: // 输入其他提示错误printf("选择错误\n");}} while (input);
我们可以使用枚举类型将功能和对应的序号关联起来,这样代码的可读性就能大大地提高了:
enum Option
{EXIT, // 0ADD, // 1DELETE, // 2SEARCH, // 3MODIFY, // 4PRINT, // 5EMPTY, // 6SORT // 7
};do {menu();printf("请选择->");scanf("%d", &input);switch (input) {case ADD: // 添加...break;case DELETE: // 删除...break;case SEARCH: // 查找...break;case MODIFY: // 修改...break;case PRINT: // 打印...break;case EMPTY: // 清空...break;case SORT: // 排序...break;case EXIT: // 退出...break;default: // 输入其他提示错误printf("选择错误\n");}} while (input);
😮 个人信息类型 PeoInfo
对于一个人的信息,基本上都是通过字符串来描述,都需要使用字符数组来存放,数组的长度可以使用宏定义来进行替换,方便后期管理。对于每个人的信息我们可以将其定义为结构体:
#define MAX_NAME 20
#define MAX_SEX 3
#define MAX_AGE 3
#define MAX_TELE 12
#define MAX_ADDRS 30typedef struct PeoInform
{char name[MAX_NAME];char sex[MAX_SEX];char age[MAX_AGE];char telephon[MAX_TELE];char addrs[MAX_ADDRS];
} PeoInform;
😮 通讯录类型 Contact
通讯录的是存放所有人的信息的,那么需要能够存放所有人的信息的空间,我们直接使用数组来实现,因为添加操作的时候我们需要标记哪个位置可以使用,所以需要使用size
来记录通讯录的大小。
#define MAX 1000 // 通讯录人数的上限typedef struct Contact
{PeoInform data[1000];int size;
} Contact;
😵💫 功能的实现
数据的类型确定了,我们就可以来实现各种功能了。
🙄 初始化通讯录 InitContact
由于通讯录是一个结构体,定义出来的又是局部变量,其内容是随机值。所以在使用之前需要先初始化一下,为了后期更好的维护,还是通过函数来进行初始化。
// 初始化
void InitContact(Contact* con)
{assert(con);con->size = 0;memset(con->data, 0, sizeof(con->data));
}
🙄 添加功能 AddContact
因为有了size
,所以新加入的联系人可以之间存放在data数组的size位置上
。如果
// 添加联系人
void AddContact(Contact* con)
{assert(con);puts("");if (con->size == MAX) // 满了就不能再添加{puts("通讯录已满!!!");}else{printf("请输入姓名 ->");scanf("%s", con->data[con->size].name);printf("请输入性别 ->");scanf("%s", con->data[con->size].sex);printf("请输入年龄 ->");scanf("%s", con->data[con->size].age);printf("请输入电话号码 ->");scanf("%s", con->data[con->size].telephon);printf("请输入地址 ->");scanf("%s", con->data[con->size].addrs);puts("");con->size++;printf("添加成功!\n");}
}
🙄 打印功能 PrintContact
添加成功后我们可以通过打印功能来进行查看,在参数方面,因为我们只是访问一下数据,所以加上const
修饰一下比较好。
// 打印联系人的信息
void PrintContact(const Contact* con)
{assert(con);printf("%-20s %-5s %-5s %-12s %-30s\n","名字", "性别", "年龄", "电话号码", "地址");for (int i = 0; i < con->size; i++) {printf("%-20s %-5s %-5s %-12s %-30s\n", con->data[i].name, con->data[i].sex, con->data[i].age, con->data[i].telephon, con->data[i].addrs);}
}
🙄 删除联系人 DeleteContact
😍 通过名字查找对应下标 FindByName
删除联系人一般是按照名字来进行删除的,所以我们需要先通过名字找到对应的下标再进行删除,因为后面需要查找的功能也需要通过名字来找人,所以这里之间实现一个函数:FindByName
// 通过名字来寻找联系人所在的下标
int FindByName(Contact* con, char* name)
{int i = 0;for (; i < con->size; i++){if (strcmp(con->data[i].name, name) == 0) return i;}return -1;
}
如果通讯录中没有该名字,提示后返回,如果有,找到后通过覆盖的方式进行删除。
// 删除指定联系人
void DeleteContact(Contact* con)
{assert(con);puts("");// 1. 找到联系人char name[MAX_NAME] = { 0 };printf("请输入需要删除的联系人的姓名->");scanf("%s", name);int pos = FindByName(con, name);// 2. 覆盖删除联系人if (pos == -1){puts("");puts("无该联系人!!!");}else{for (int i = pos; i < con->size - 1; i++) {con->data[i] = con->data[i + 1];}con->size--;printf("删除成功!\n");}
}
🙄 查找联系人 SearchContact
通过名字查找,找到后直接输出信息,没找到提示后返回
// 查找指定联系人
void SearchContact(const Contact* con)
{assert(con);puts("");// 1. 找到联系人char name[MAX_NAME] = { 0 };printf("请输入需要删除的联系人的姓名->");scanf("%s", name);int pos = FindByName(con, name);if (pos == -1){puts("找不到该联系人???");}else{printf("%-20s %-5s %-5s %-12s %-30s\n","名字", "性别", "年龄", "电话号码", "地址");printf("%-20s %-5s %-5s %-12s %-30s\n",con->data[pos].name, con->data[pos].sex,con->data[pos].age, con->data[pos].telephon,con->data[pos].addrs);}
}
🙄 修改联系人信息 ModifyContact
名字作为关键字来修改个人信息。
// 修改指定联系人信息
void ModifeContact(Contact* con)
{assert(con);puts("");// 1. 找到联系人char name[MAX_NAME] = { 0 };printf("请输入需要删除的联系人的姓名->");scanf("%s", name);int pos = FindByName(con, name);if (pos == -1){puts("找不到该联系人???");}else{printf("请输入姓名 ->");scanf("%s", con->data[pos].name);printf("请输入性别 ->");scanf("%s", con->data[pos].sex);printf("请输入年龄 ->");scanf("%s", con->data[pos].age);printf("请输入电话号码 ->");scanf("%s", con->data[pos].telephon);printf("请输入地址 ->");scanf("%s", con->data[pos].addrs);printf("\n");printf("修改成功!\n");printf("%-20s %-5s %-5s %-12s %-30s\n", "名字", "性别", "年龄", "电话号码", "地址");printf("%-20s %-5s %-5s %-12s %-30s\n", con->data[pos].name, con->data[pos].sex, con->data[pos].age, con->data[pos].telephon, con->data[pos].addrs);}
}
🙄 清空通讯录 EmptyContact
因为全部的访问操作都跟size
有关,所以清空时只需要将size
置为0即可。
// 清空通讯录
void EmptyContact(Contact* con)
{assert(con);con->size = 0;printf("清空成功!\n");
}
🙄 排序功能 SortContact
还是老样子,用名字作为关键字,这里我使用的是冒泡排序,相对简单一点。
// 通过姓名排序
void SortContact(Contact* con)
{assert(con);for (int i = 0; i < con->size; i ++)for (int j = 0; j < con->size - 1 - i; j++){if (strcmp(con->data[j].name, con->data[j + 1].name) > 0){PeoInform temp = con->data[j];con->data[j] = con->data[j + 1];con->data[j + 1] = temp;}}puts("排序成功");
}
🌈 总结
以上就是第一个版本的通讯录的实现了。看似简单,但是对C语言的基础知识也是有一定的考验的,想要打牢基础还是要多写写联系一下的。
源码地址:静态数组版通讯录