参考教程:黑马程序员——01 通讯录管理系统 - 系统功能介绍及展示_bilibili
注意这个项目其实是想让我用链表实现的,但我没仔细看要求(说实话,一个通讯录要预先分配数十到数百倍于真实使用的空间的内存,想想就恐怖 XP
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) {
//input name
printf("请输入姓名:\n");
scanf("%s", arr[i].name);
//input gender
printf("请输入性别:\n");
scanf("%s", arr[i].gender);
//input age
printf("请输入年龄:\n");
scanf("%d", &arr[i].age);
//input phone number
printf("请输入联系电话:\n");
scanf("%s", arr[i].phone);
//input location
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
25void 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
22void 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
4while (scanf("%d", &status) != 1) {
printf("无法识别功能代码,请重新输入:\n");
while (getchar() != '\n');//将新数据传入缓冲区,让scanf函数读取
}
程序首先检查scanf返回的成功匹配参数的数据个数。当用户输入一个以数字开头的数据时,scanf返回1,程序进入功能代码匹配环节;当用户输入一个以其他字符开头的数据时,scanf返回0,进入该循环。
对于不存在的功能代码,提示 “功能不存在”:1
2default:
printf("功能不存在。请重试!\n");

