目录
环境:Visual Studio2022
实现语言:C语言
一、具体实现步骤
(一)创建工程
step1.首先我们需要创建一个工程,打开编译器,点击创建新项目
step2.选择空项目,点击下一步
step3.为项目起一个合适的名字 ,我这里就叫Contact ,然后点击创建
step4.来到新的页面后 找到解决方案资源管理器 分别右键源文件、头文件 选择添加新建项,分别建好contact.h contact.c test.c 三个文件
解释:
contact.h
- 统一包含 工程所需要包含的头文件
- 将未来可能修改大小的值 定义成 标识符常量
- 声明自定义的数据类型
- 通讯录功能 函数的声明
contact.c
- 实现通讯录功能的函数
test.c
- 用来测试通讯录的功能
(二)设计存储数据的结构形式
首先设计一下存储数据的结构形式,我们采用的是顺序表,简单点说,就是用数组存储每一个人的信息,再加上一个用来记录当前通讯录中包含多少个人的信息的变量 就构成了一个通讯录,我们将它自定义为一个通讯录类型的数据
//声明并类型重命名 一个通讯录的 结构体类型
//该类型包含
//一个 Peoinfo类型的 结构体数组 data[] 用来存放每一个人的信息
//一个 sz变量 用来记录当前通讯录中包含多少个人的信息
typedef struct Contact
{
Peoinfo data[DATA_MAX];
int sz;
}Contact;
然后我们发现,每个人的信息包括:姓名、性别、年龄、电话、住址 这些条目,所以我们将每个人的信息打包。
//声明 一个 可以包含一条人信息的 结构体类型
//并进行类型重命名 重命名为Peoinfo
typedef struct Peoinfo
{
char name[NAME_MAX];
char sex[SEX_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}Peoinfo;
(三)函数功能设计及实现
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 清空所有联系人
- 以名字排序所有联系人
设计函数的形参实现,在头文件中声明函数
//初始化通讯录
void InitContact(Contact* con);
//向通讯录中添加信息
void AddContact(Contact* con);
//展示通讯录内容
void ShowContact(Contact* con);
//删除通讯录内容
void DelContact(Contact* con);
//查找通讯录内容
void SearchContact(Contact* con);
//修改通讯录内容
void ModifyContact(Contact* con);
//清空通讯录
void EmptyContact(Contact* con);
//排序通讯录 按名字 升序
void SortContact(Contact* con);
在Contact.c中具体实现这些函数
1.初始化函数
初始化时,传的是结构体Con的地址,用结构体指针pc来接收,里面并用aasert断言一下,以便知道是哪行出了问题,它属于一种暴力检查。再用memset函数,把通讯录里面的信息条数置为0,即把sz记录当前数量置为0。
void InitContact(Contact* pc)
{
assert(pc);
pc->sz = 0;
memset(pc->data,0,sizeof(pc->data));
}
2.添加通讯录信息的函数
- 用结构体指针变量pc接收,直接用标准输入输出函数,去录入信息,如名字,用结构体指针pc访问数组,并用pc访问成员变量sz找到数组下标,再.name找到名字。
- 里面要注意再用scanf的时候 成员变量如果是数组,数组名就是首元素地址,不用再加&取地址运算符,而成员变量如果是整型变量要加取地址符号。
- 最后用sz++,记录已录人数,并打印成功添加。
void AddContact(Contact* pc)
{
assert(pc);
//增加联系人之前 应先判断通讯录是否满了
if (pc->sz == DATA_MAX)
{
printf("通讯录已满,不能执行添加操作!\n");
return;
}
//增加信息
printf("联系人姓名:");
scanf("%s", pc->data[pc->sz].name);
printf("联系人性别:");
scanf("%s", pc->data[pc->sz].sex);
printf("联系人年龄:");
scanf("%d", &(pc->data[pc->sz].age));
printf("联系人电话:");
scanf("%s", pc->data[pc->sz].tel);
printf("联系人住址:");
scanf("%s", pc->data[pc->sz].addr);
//增加成功
pc->sz++;
printf("增加成功!\n");
}
3.展示通讯录内容
- 还是要实现对齐保持上下一致,用for循环直接打印全部信息。
void ShowContact(Contact* pc)
{
assert(pc);
//打印之前判断通讯录中是否存在联系人
if (pc->sz == 0)
{
printf("通讯录为空,无需打印!\n");
}
int i = 0;
printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
for (i = 0; i < pc->sz; i++)
{
printf("%-20s %-20s %-20d %-20s %-20s\n",
pc->data[i].name, pc->data[i].sex,
pc->data[i].age, pc->data[i].tel,
pc->data[i].addr);
}
}
4.删除通讯录内容
- 在里面要先创建name数组,来存放要删除的名字,之后需要判断要删除的人是否存在还需创建Find_name()函数来判断,并用ret接收。
- 找到之后用for循环把后面的元素左移。
- 最后人数减一。
- 此函数如下:
void DelContact(Contact* pc)
{
assert(pc);
char name[20];
printf("请输入你要删除的联系人姓名:");
scanf("%s", name);
//查找联系人是否存在
//封装FindPeoInfo_ByName()函数 如果存在返回下标 如果不存在返回-1
int ret = FindPeoInfo_ByName(name, pc);
if (ret == -1)
{
printf("该联系人不存在!\n");
}
else
{
int i = 0;
//为什么是pc->sz-1?
//1000个元素 下标0~999
//i < pc->sz-1 意味着只能访问到 i(max)=998
//pc->data[998] = pc->data[999];才不会发生下标越界
for (i = ret; i < pc->sz - 1; i++)
{
pc->data[i] = pc->data[i + 1];
}
//若删除最后一个元素 最后一个元素不用被覆盖 pc->sz--即可
pc->sz--;
printf("删除成功!\n");
}
}
解析里面用到的FindPeoInfo_ByName():
除了用pc接收,还要接收删除name的地址,在里面用for循环,遍历查找,里面再用if判断语句来判断,用strcmp比较函数来比较所输入名字和通讯录名字是否一样。
如果一样返回下标。没有找到返回-1。
//遍历data数组 寻找姓名字符串一样的数组下标
//如果存在返回下标 如果不存在返回-1
static int FindPeoInfo_ByName(char* name, Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->data[i].name) == 0)
{
return i;
}
}
return -1;
}
查找通讯录内容
- 前面的步骤和删除步骤一样,需要判断搜索的人是否存在,如果存在打印。
- 为了让信息显示的更美观和直观,用对齐方式来实现且上下两个打印函数保持一致,负号表示右对齐。
void SearchContact(Contact* pc)
{
assert(pc);
char name[20];
printf("请输入你要查找的联系人姓名:");
scanf("%s", name);
int ret = FindPeoInfo_ByName(name, pc);
if (ret == -1)
{
printf("该联系人不存在!\n");
}
else
{
printf("该联系人存在:\n