C语言通讯录功能的实现
一、项目需求:
我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。
并且在通讯录中要求实现下述功能:
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 以名字排序所有联系人
二、模块化代码
在代码编辑中,为了避免代码过于冗长,也为了便于后续功能的修改。我们一般将代码分多成多个模块。
建立如下文件
contact.h (头文件,用来引入库函数,存放声明) contact.c (实现通讯录每个部分的功能) test.c (主函数,用于测试通讯录功能是否成功实现)
注意:.c文件需要使用自定义头文件的声明,要在最前端引入
#include "contact.h" //自定义头文件需要用"" 而不是<>
三、整体思路
1.创建结构体类型
我们需要一个通讯录,可以用来存储1000个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址。
这里我们就需要建立一个结构体,描述这信息。
#define nameMax 15
#define sexMax 5
#define numberMax 15
#define addrMax 20
//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息
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功能实现一下。
#define _CRT_SECURE_NO_WARNINGS
#include "contact.h"
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
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
#define nameMax 15
#define sexMax 5
#define numberMax 15
#define addrMax 20
//结构体定义,表示通讯录的每一页,包含了一个联系人的所有信息
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
#include "contact.h"
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
#include "contact.h"
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函数?
- 通讯录结构体是否可以使用动态内存管理?
- 如何添加文件功能? 这些都是值得去进一步学习和改进的地方。