C语言通讯录功能的实现
一、项目需求:
我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。
并且在通讯录中要求实现下述功能:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 以名字排序所有联系人
二、模块化代码
在代码编辑中,为了避免代码过于冗长,也为了便于后续功能的修改。我们一般将代码分多成多个模块。
建立如下文件
contact.h (头文件,用来引入库函数,存放声明) contact.c (实现通讯录每个部分的功能) test.c (主函数,用于测试通讯录功能是否成功实现)
注意:.c文件需要使用自定义头文件的声明,要在最前端引入
#include "contact.h" //自定义头文件需要用"" 而不是<>
三、整体思路
1.创建结构体类型
我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。
这里我们就需要建立一个结构体,描述这信息。
//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息 | |
typedef struct inform | |
{ | |
char name[nameMax]; | |
char sex[sexMax]; | |
int age; | |
char number[numberMax]; | |
char addr[addrMax]; | |
}inform; |
这里我使用了typedef关键字关于typedef:
typedef 是类型重命名 与struct 结构体 连起来使用,可以让struct 在定义完成后使用{}后的名字作为类型名。
一个人的通讯录信息结构体已经已经建立好了。现在我们需要一个结构体数组,来存放1000个人的信息,同时出于功能的需求,我们还要知道通讯录已经存储了多少个人的信息,所以我们再建立一个通讯录本结构体。
//定义整本通讯录,能够存储100个人的信息和其通讯录所在位置 | |
typedef struct Contact | |
{ | |
inform date[1000]; | |
int num; | |
}Contact; |
这里使用了结构体嵌套,结构体内部元素是一个结构体数组;
2.建立循环
首先我们要有一个菜单,向使用者展示通讯录的功能,并且提示他们可以输入对应的字符来进行相对应的功能。
并且这是一个循环,可以不断输入来选择功能。
void meanu() | |
{ | |
printf("********************************\n"); | |
printf("*** 1. add 2.del ***\n"); | |
printf("*** 3.search 4.modify ***\n"); | |
printf("*** 5.show 6.sort ***\n"); | |
printf("*** 0.exit ***\n"); | |
printf("********************************\n"); | |
} | |
int main() | |
{ | |
int input = 0; | |
do | |
{ | |
meanu(); | |
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: | |
break; | |
} | |
} while (input); | |
return 0; | |
} |
建立了菜单循环,下面就要开始往每一个选择加入不同的功能。
但是我们在编写程序1,2,3,4…这样的数字,经常需要回头查看设置的功能是什么
我们可以使用枚举常量
关于枚举常量 枚举常量有点类似于宏定义#define,是将一系列名字定义为常量。其默认赋值顺序是从0开始,逐个+1 缺点:只能赋值为整形常量。
enum | |
{ | |
Exit, | |
Add, | |
Del, | |
Search, | |
Modify, | |
Show, | |
Sort, | |
}; |
写在contact.h头文件中
那么循环可以更改为上述名字。
我们在将exit功能和default功能实现一下。
void meanu() | |
{ | |
printf("**********************************\n"); | |
printf("*** 1. add 2.del ***\n"); | |
printf("*** 3.search 4.modify ***\n"); | |
printf("*** 5.show 6.sort ***\n"); | |
printf("*** 0.exit ***\n"); | |
printf("**********************************\n"); | |
} | |
int main() | |
{ | |
int input = 0; | |
do | |
{ | |
meanu(); | |
scanf("%d", &input); | |
switch (input) | |
{ | |
case Add: | |
break; | |
case Del: | |
break; | |
case Search: | |
break; | |
case Modify: | |
break; | |
case Show: | |
break; | |
case Sort: | |
break; | |
case Exit: | |
printf("退出成功!!!\n"); | |
break; | |
default: | |
printf("您输入错误,请重新输入\n"); | |
break; | |
} | |
} while (input); | |
return 0; | |
} |
注意!!!
初始化结构体之后,结构体的初始值 !=0,需要主动初始化(赋予空白内容)
并且结构体的函数传递必须为 传址传递 否则无法修改结构体内部的信息。
//结构体初始化函数 | |
void initContact(Contact* p) | |
{ | |
//先判断穿过来的指针是否为空 | |
assert(p); | |
p->num = 0; | |
memsent(p->date,0,sizeof(p->date)); | |
} |
利用memsent函数 可以快速的将date数组里的内容进行初始化
3.各个功能的实现
(1)add()增添联系人
//新增联系人 | |
void add(Contact* p) | |
{ | |
assert(p); | |
//判断通讯录是否已经未满,如果num = 100则无法添加 | |
if (1000 == p->num) | |
{ | |
printf("通讯录已满,无法进行添加\n"); | |
return ; | |
} | |
printf("请输入姓名:\n"); | |
scanf("%s",p->date[p->num].name); | |
printf("请输入性别:\n"); | |
scanf("%s",p->date[p->num].sex); | |
printf("请输入年龄:\n"); | |
scanf("%d",&(p->date[p->num].age)); | |
printf("请输入号码:\n"); | |
scanf("%s",p->date[p->num].number); | |
printf("请输入地址:\n"); | |
scanf("%s",p->date[p->num].addr); | |
p->num += 1; | |
printf("联系人信息已添加完成!\n"); | |
} |
注意:
在新增联系人的时候,我们首先需要判断通讯录是否为满。如果为满则跳出并给出提示。
(2)show()显示通讯录信息
void show(Contact* p) | |
{ | |
assert(p); | |
if (0 == p->num) | |
{ | |
printf("没有可以显示的信息!\n"); | |
return ; | |
} | |
printf("%-15s %-5s %-5s %-15s %-20s\n","姓名","性别","年龄","电话","地址"); | |
int i = 0; | |
for (i = 0; i < p->num; i++) | |
{ | |
printf("%-15s %-5s %-5d %-15s %-20s\n", | |
p->date[i].name, p->date[i].sex, p->date[i].age, p->date[i].number, p->date[i].addr); | |
} | |
} |
如果结构体都为0,则不需要打印。
(3)search查找通讯录信息
这里我们考虑的是按人名进行查找,由于后面的删除功能也需要进行按人名进行查找,所以先自定义一个按名索引函数
按名索引 让用户输入要进行操作的联系人的名字,通过strcmp函数比较是否相同 如果相同 return 其所在位置 如果不同 return -1
//构建一个内部函数,用于实现按名查找 | |
int findName(Contact* p , char name[]) | |
{ | |
assert(p); | |
int i = 0; | |
//遍历通讯录,找到了返回是第几个联系人 | |
for (i = 0; i < p->num; i++) | |
{ | |
if (0 == strcmp(p->date[i].name, name)) | |
return i; | |
} | |
//如果找不到返回 -1 | |
return -1; | |
} |
由于这个函数仅供该文件下的函数调用,所以不需要在头文件中声明也是可以的。
有了按名索引功能,搜索功能就简单了许多:
但是仍有需要注意的是:
- 首先通讯里要保证有内容可查,如果没有则进行提示
- 查找到后要进行打印(同show函数的功能)
//查找联系人的函数 | |
void search(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax] ; | |
if (p->num == 0) | |
{ | |
printf("通讯录无数据可供查找!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要查找人的姓名:"); | |
scanf("%s",name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
printf("查找成功:\n"); | |
//显示查找的人的信息: | |
printf("%-15s %-5s %-5s %-15s %-20s\n", | |
"姓名", "性别", "年龄", "电话", "地址"); | |
printf("%-15s %-5s %-5d %-15s %-20s\n", | |
p->date[pos].name, | |
p->date[pos].sex, | |
p->date[pos].age, | |
p->date[pos].number, | |
p->date[pos].addr); | |
} |
(4)del删除通讯录信息功能
删除通讯录是建立在查找功能之上的,先对输入的人名进行按名索引,随换把要删除的信息进行覆盖,让后面的每一个元素都向前移动1位
计算机信息删除的本质,其实也就是把不可修改的区域进行覆盖,使其变为可修改的区域。
代码如下:
//删除联系人 | |
void del(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax]; | |
if (p->num == 0) | |
{ | |
printf("通讯录为空!!!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要删除人的姓名:"); | |
scanf("%s", name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
//删除 | |
int i = 0; | |
for (i = pos; i < p->num; i++) | |
{ | |
p->date[i] = p->date[i + 1]; //同类型之间的变量可以进行赋值操作 | |
} | |
p->num--; //防止数组越界 | |
printf("清楚完毕!!\n"); | |
} |
(5)modify修改信息功能
//对通讯录信息进行修改 | |
void modify(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax]; | |
if (p->num == 0) | |
{ | |
printf("通讯录为空!!!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要修改人的姓名:"); | |
scanf("%s", name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
//修改: | |
int choose = 0; | |
do | |
{ | |
//选择修改菜单: | |
printf("………………………………………………\n"); | |
printf("*** 1.姓名 2.性别 ***\n"); | |
printf("*** 3.年龄 4.号码 ***\n"); | |
printf("*** 5.地址 ***\n"); | |
printf("*** 0.退出修改 ***\n"); | |
printf("………………………………………………\n"); | |
printf("请输入你要修改的选项:>"); | |
scanf("%d", &choose); | |
switch (choose) | |
{ | |
case 1: | |
printf("请输入修改后的姓名:"); | |
scanf("%s",p->date[pos].name); | |
break; | |
case 2: | |
printf("请输入修改后的性别:"); | |
scanf("%s",p->date[pos].sex); | |
break; | |
case 3: | |
printf("请输入修改后的年龄:"); | |
scanf("%d",p->date[pos].age); | |
break; | |
case 4: | |
printf("请输入修改后的号码:"); | |
scanf("%s",p->date[pos].number); | |
break; | |
case 5: | |
printf("请输入修改后的地址:"); | |
scanf("%s",p->date[pos].addr); | |
break; | |
case 0: | |
printf("修改成功!!!\n"); | |
break; | |
default : | |
printf("非法字符输入,请重新选择\n"); | |
break; | |
} | |
} while (choose); | |
} |
前面的功能基本一直,按名索引查找被操作人所在位置
后面类比主函数功能,建立一个修改信息菜单。直接对通讯录里的信息修改
(6)sort函数排序
//sort排序功能(按照名字排升序) | |
void sort(Contact* p) | |
{ | |
assert(p); | |
Contact temp; | |
if (p->num == 0) | |
printf("通讯录为空,无法排序!!\n"); | |
//冒泡排序 | |
int i = 0; | |
int j = 0; | |
for (i = 0; i < p->num; i++) | |
{ | |
for (j = 0; j < p->num - i - 1; j++) | |
{ | |
if (strcmp(p->date[j].name, p->date[j + 1].name) > 0) | |
{ | |
temp.date[0] = p->date[i]; | |
p->date[i] = p->date[i + 1]; | |
p->date[i + 1] = temp.date[i]; | |
} | |
} | |
} | |
} |
排序的选择有很多种,可以按年龄排序,也可以自己进行选择
四、代码汇总
contact.h
//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息 | |
typedef struct inform | |
{ | |
char name[nameMax]; | |
char sex[sexMax]; | |
int age; | |
char number[numberMax]; | |
char addr[addrMax]; | |
}inform; | |
//定义整本通讯录,能够存储100个人的信息和其通讯录所在位置 | |
typedef struct Contact | |
{ | |
inform date[1000]; | |
int num; | |
}Contact; | |
enum | |
{ | |
Exit, | |
Add, | |
Del, | |
Search, | |
Modify, | |
Show, | |
Sort, | |
}; | |
//初始化结构体 | |
void initContact(Contact* p); | |
//加入联系人 | |
void add(Contact* p); | |
//显示联系人 | |
void show(Contact* p); | |
//删除联系人 | |
void del(Contact* p); | |
//查找联系人 | |
void search(Contact* p); | |
//对通讯录信息进行修改 | |
void modify(Contact* p); | |
//sort排序功能 | |
void sort(Contact* p); |
contact.c
void initContact(Contact* p) | |
{ | |
//先判断穿过来的指针是否为空 | |
assert(p); | |
p->num = 0; | |
memset(p->date,0,sizeof(p->date)); | |
} | |
//新增联系人 | |
void add(Contact* p) | |
{ | |
assert(p); | |
//判断通讯录是否已经未满,如果num = 100则无法添加 | |
if (1000 == p->num) | |
{ | |
printf("通讯录已满,无法进行添加\n"); | |
return ; | |
} | |
printf("请输入姓名:\n"); | |
scanf("%s",p->date[p->num].name); | |
printf("请输入性别:\n"); | |
scanf("%s",p->date[p->num].sex); | |
printf("请输入年龄:\n"); | |
scanf("%d",&(p->date[p->num].age)); | |
printf("请输入号码:\n"); | |
scanf("%s",p->date[p->num].number); | |
printf("请输入地址:\n"); | |
scanf("%s",p->date[p->num].addr); | |
p->num += 1; | |
printf("联系人信息已添加完成!\n"); | |
} | |
//展示通讯录信息 | |
void show(Contact* p) | |
{ | |
assert(p); | |
if (0 == p->num) | |
{ | |
printf("没有可以显示的信息!\n"); | |
return ; | |
} | |
printf("%-15s %-5s %-5s %-15s %-20s\n", | |
"姓名","性别","年龄","电话","地址"); | |
int i = 0; | |
for (i = 0; i < p->num; i++) | |
{ | |
printf("%-15s %-5s %-5d %-15s %-20s\n", | |
p->date[i].name, | |
p->date[i].sex, | |
p->date[i].age, | |
p->date[i].number, | |
p->date[i].addr); | |
} | |
} | |
//构建一个内部函数,用于实现按名查找 | |
int findName(Contact* p , char name[]) | |
{ | |
assert(p); | |
int i = 0; | |
//遍历通讯录,找到了返回是第几个联系人 | |
for (i = 0; i < p->num; i++) | |
{ | |
if (0 == strcmp(p->date[i].name, name)) | |
return i; | |
} | |
//如果找不到返回 -1 | |
return -1; | |
} | |
//查找联系人的函数 | |
void search(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax] ; | |
if (p->num == 0) | |
{ | |
printf("通讯录无数据可供查找!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要查找人的姓名:"); | |
scanf("%s",name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
printf("查找成功:\n"); | |
//显示查找的人的信息: | |
printf("%-15s %-5s %-5s %-15s %-20s\n", | |
"姓名", "性别", "年龄", "电话", "地址"); | |
printf("%-15s %-5s %-5d %-15s %-20s\n", | |
p->date[pos].name, | |
p->date[pos].sex, | |
p->date[pos].age, | |
p->date[pos].number, | |
p->date[pos].addr); | |
} | |
//删除联系人 | |
void del(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax]; | |
if (p->num == 0) | |
{ | |
printf("通讯录为空!!!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要删除人的姓名:"); | |
scanf("%s", name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
//删除 | |
int i = 0; | |
for (i = pos; i < p->num; i++) | |
{ | |
p->date[i] = p->date[i + 1]; //同类型之间的变量可以进行赋值操作 | |
} | |
p->num--; //防止数组越界 | |
printf("清楚完毕!!\n"); | |
} | |
//对通讯录信息进行修改 | |
void modify(Contact* p) | |
{ | |
assert(p); | |
char name[nameMax]; | |
if (p->num == 0) | |
{ | |
printf("通讯录为空!!!\n"); | |
printf("请从新进行操作!\n"); | |
return; | |
} | |
printf("请输入你要修改人的姓名:"); | |
scanf("%s", name); | |
//rer用来接收findName的返回值,即所查人所在的位置 | |
int pos = findName(p, name); | |
//如果返回值为 -1 表示 | |
if (-1 == pos) | |
{ | |
printf("没有找到这个人的信息!\n"); | |
return; | |
} | |
//修改: | |
int choose = 0; | |
do | |
{ | |
//选择修改菜单: | |
printf("………………………………………………\n"); | |
printf("*** 1.姓名 2.性别 ***\n"); | |
printf("*** 3.年龄 4.号码 ***\n"); | |
printf("*** 5.地址 ***\n"); | |
printf("*** 0.退出修改 ***\n"); | |
printf("………………………………………………\n"); | |
printf("请输入你要修改的选项:>"); | |
scanf("%d", &choose); | |
switch (choose) | |
{ | |
case 1: | |
printf("请输入修改后的姓名:"); | |
scanf("%s",p->date[pos].name); | |
break; | |
case 2: | |
printf("请输入修改后的性别:"); | |
scanf("%s",p->date[pos].sex); | |
break; | |
case 3: | |
printf("请输入修改后的年龄:"); | |
scanf("%d",p->date[pos].age); | |
break; | |
case 4: | |
printf("请输入修改后的号码:"); | |
scanf("%s",p->date[pos].number); | |
break; | |
case 5: | |
printf("请输入修改后的地址:"); | |
scanf("%s",p->date[pos].addr); | |
break; | |
case 0: | |
printf("修改成功!!!\n"); | |
break; | |
default : | |
printf("非法字符输入,请重新选择\n"); | |
break; | |
} | |
} while (choose); | |
} | |
//sort排序功能(按照名字排升序) | |
void sort(Contact* p) | |
{ | |
assert(p); | |
Contact temp; | |
if (p->num == 0) | |
printf("通讯录为空,无法排序!!\n"); | |
//冒泡排序 | |
int i = 0; | |
int j = 0; | |
for (i = 0; i < p->num; i++) | |
{ | |
for (j = 0; j < p->num - i - 1; j++) | |
{ | |
if (strcmp(p->date[j].name, p->date[j + 1].name) > 0) | |
{ | |
temp.date[0] = p->date[i]; | |
p->date[i] = p->date[i + 1]; | |
p->date[i + 1] = temp.date[i]; | |
} | |
} | |
} | |
} |
test.c
void meanu() | |
{ | |
printf("**********************************\n"); | |
printf("*** 1.add 2.del ***\n"); | |
printf("*** 3.search 4.modify ***\n"); | |
printf("*** 5.show 6.sort ***\n"); | |
printf("*** 0.exit ***\n"); | |
printf("**********************************\n"); | |
} | |
int main() | |
{ | |
int input = 0; | |
//创建通讯录 | |
Contact contact; | |
//注意:结构体类型创建的时候初始值是一个随机值,不是0, | |
//需要对结构体进行初始化 | |
initContact(&contact); | |
do | |
{ | |
meanu(); | |
scanf("%d", &input); | |
switch (input) | |
{ | |
case Add: | |
add(&contact); | |
break; | |
case Del: | |
del(&contact); | |
break; | |
case Search: | |
search(&contact); | |
break; | |
case Modify: | |
modify(&contact); | |
break; | |
case Show: | |
show(&contact); | |
break; | |
case Sort: | |
break; | |
case Exit: | |
printf("退出成功!!!\n"); | |
break; | |
default: | |
printf("您输入错误,请重新输入\n"); | |
break; | |
} | |
} while (input); | |
return 0; | |
} |
五、反思和总结
这里只是一个简单的C语言项目,里面还有很多可以改进的地方。 如:
- 排序函数是否可以用qsort函数?
- 通讯录结构体是否可以使用动态内存管理?
- 如何添加文件功能? 这些都是值得去进一步学习和改进的地方。