全文目录
- 😀 前言
- 🤔 模块和功能划分
- 🤨 数据类型的选择
- 😮 功能序号类型 `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, // 0
ADD, // 1
DELETE, // 2
SEARCH, // 3
MODIFY, // 4
PRINT, // 5
EMPTY, // 6
SORT // 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 30
typedef 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语言的基础知识也是有一定的考验的,想要打牢基础还是要多写写联系一下的。
源码地址:静态数组版通讯录