参考教程:黑马程序员 ——01 通讯录管理系统 - 系统功能介绍及展示_bilibili
1. 项目功能介绍
对于一个简单的通讯录管理系统,其主要功能有:
- 添加联系人:向通讯录中添加新联系人,固定需要添加的信息有:姓名、性别、年龄、联系电话和家庭住址。要求最多支持添加 1000 个联系人。
- 显示联系人:显示通讯录中所有联系人的信息
- 删除联系人:按照姓名删除指定的联系人
- 查找联系人:按照姓名查看指定联系人信息
- 修改联系人:按照姓名重新修改指定联系人
- 清空联系人:清空通讯录中所有联系人信息
- 退出通讯录:退出当前使用的通讯录
该通讯录应该进行具备一定的人机交互特征(命令行下),具体有:
-
初始的功能菜单:列出以上功能,并用数字代号提示各个功能。用户可以输入代号来访问对应的功能。可以对菜单做一些装饰性元素
-
不重启切换功能:要求切换功能时保持当前程序运行,而不是让用户一遍遍重启程序。菜单不要重复输出
-
添加联系人:以问答式方式引导用户输入需要添加的信息。性别一栏可以提供选项,如果要考虑 DEI,也可以让用户自己输入自己的性别。所有信息添加完成后,需要明确提示用户 “添加成功”。
-
显示联系人:要求格式化输出。建议在一行内输出且只输出一个联系人的信息。如果没有任何联系人信息,应输出 “当前记录为空” 之类的提示信息
-
删除联系人:以问答式方式确认需要删除的联系人。删除成功后需要说明。如果删除了不存在的联系人,程序也需要提示 “查无此人”。
-
查找联系人:以问答式方式确认需要查询的联系人。如果查询了不存在的联系人,程序需要提示 “查无此人”。
-
修改联系人:实质上相当于调用 “添加联系人” 功能。修改成功后需要提示
-
清空联系人:清空后需要提示。更好的做法是在删除前加上确认信息,防止误删
-
退出通讯录:建议输出退出语,比如 “欢迎下次使用”
2. 分功能开发
2.1 添加联系人
我们使用结构体来存储单个联系人信息,因为我们要存储的信息的类型明显不同,并且应该归属于同一个条目下。
对于联系人信息结构体的集合,我们使用数组存储。
结构体数组之前没有介绍,可参见:C 结构体 | 菜鸟教程下面列出的笔记,以及 C 语言数组、结构体、结构体数组作为函数参数_结构体数组作为函数参数传递 - CSDN 博客
使用最经典的 scanf
输入单个结构体的成员的数据。提示语用 printf
输出。
单个信息输入完毕后,在 main
函数中对计数器 i++
,标示数组元素个数以供索引
不得不说,Clang 的代码诊断对于中文疑似不太友好啊,一直输出 warning
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void add_person(info arr[], int i) { printf("请输入姓名:\n"); scanf("%s", arr[i].name); printf("请输入性别:\n"); scanf("%s", arr[i].gender); printf("请输入年龄:\n"); scanf("%d", &arr[i].age); printf("请输入联系电话:\n"); scanf("%s", arr[i].phone); printf("请输入家庭住址:\n"); scanf("%s", arr[i].location); }
|
实机演示:

2.2 显示联系人
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void list_person(info arr[], int i) { if (i == 0) { printf("当前记录为空\n"); } else { for (int cnt = 0; cnt < i; cnt++) { printf("姓名:%s \ 性别:%s \ 年龄:%d \ 联系电话:%s \ 家庭住址:%s\n", arr[cnt].name, arr[cnt].gender, arr[cnt].age, arr[cnt].phone, arr[cnt].location); } } }
|
实机演示:


2.3 删除联系人
初始版本仅仅令计数器 i--
,但在后续测试中发现只考虑到最后一个联系人数据的删除。
为了让任意一个元素均可以被删除,我们可以移动数组内元素,用后续元素把目标元素给覆盖掉,再令计数器 i--
,防止数组越界
由于我们需要改变实参 i
的数值,因此传入 i
的指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void delete_person(info arr[], int* i) { bool check = 0; if (*i == 0) { printf("当前通讯录为空\n"); } else { printf("请输入将要删除的联系人的姓名:\n"); char goal[MAX_CHARACTER_LENGTH]; scanf("%s", goal); for (int j = 0; j < *i; j++) { if (!strcmp(goal, arr[j].name)) { for (int ds = 0; ds < *i - j; ds++) { arr[j + ds] = arr[j + (ds + 1)]; } (*i)--; check = 1; printf("删除成功!\n"); break; } } if (!check){ printf("查无此人\n"); } } }
|
实机演示:

2.4 查找联系人
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void find_person(info arr[], int i) { printf("请输入将要查询的联系人的姓名:\n"); char goal[MAX_CHARACTER_LENGTH]; scanf("%s", goal); for (int j = 0; j < i; j++) { if (!strcmp(goal, arr[j].name)) { printf("姓名:%s \ 性别:%s \ 年龄:%d \ 联系电话:%s \ 家庭住址:%s\n", arr[j].name, arr[j].gender, arr[j].age, arr[j].phone, arr[j].location); } else { printf("查无此人\n"); } } }
|


2.5 修改联系人
找到目标联系人后调用 add_person
把原来的联系人覆盖掉就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| void edit_person(info arr[], int i) { bool check = 0; if (i == 0) { printf("当前通讯录为空\n"); } else { printf("请输入将要修改的联系人的姓名:\n"); char goal[MAX_CHARACTER_LENGTH]; scanf("%s", goal); for (int j = 0; j < i; j++) { if (!strcmp(goal, arr[j].name)) { add_person(arr, j); check = 1; break; } } if (!check) { printf("查无此人\n"); } } }
|
实机演示:

2.6 清空联系人
1 2 3 4 5 6 7 8 9 10 11 12
| void empty_person(int* i) { printf("确定要清空通讯录吗?\n" "输入'y'确定,输入'n'取消\n"); char check = getchar(); if (check == 'y' || check == 'Y') { *i = 0; printf("已清空!\n"); } else { printf("已取消操作\n"); } }
|

2.7 主界面(退出通讯录)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| int main(void) { int status; int i = 0; do { printf("*****通讯录*****\n"); printf("*请输入对应代码*\n" "* 1.添加联系人 *\n" "* 2.显示联系人 *\n" "* 3.删除联系人 *\n" "* 4.查找联系人 *\n" "* 5.修改联系人 *\n" "* 6.清空联系人 *\n" "* 0.退出通讯录 *\n" "****************\n"); while (scanf("%d", &status) != 1) { printf("无法识别功能代码,请重新输入:\n"); while (getchar() != '\n'); } switch (status) { case 1: add_person(data, i); i++; break; case 2: list_person(data, i); break; case 3: delete_person(data, &i); break; case 4: find_person(data, i); break; case 5: edit_person(data, i); break; case 6: empty_person(&i); break; case 0: printf("欢迎下次使用!\n"); break; default: printf("功能不存在。请重试!\n"); } system("pause"); system("cls"); } while (status != 0); return 0; }
|

3. 人机交互优化
3.1 提示非法输入
在主界面中,对于非数字输入,我们将拒绝该输入并要求用户重新输入:
1 2 3 4
| while (scanf("%d", &status) != 1) { printf("无法识别功能代码,请重新输入:\n"); while (getchar() != '\n'); }
|
程序首先检查 scanf
返回的成功匹配参数的数据个数。当用户输入一个以数字开头的数据时,scanf
返回 1
,程序进入功能代码匹配环节;当用户输入一个以其他字符开头的数据时,scanf
返回 0
,进入该循环。
对于不存在的功能代码,提示 “功能不存在”:
1 2
| default: printf("功能不存在。请重试!\n");
|
