个人主页:秋风起,再归来~
文章专栏:C语言实战项目
个人格言:悟已往之不谏,知来者犹可追
克心守己,律己则安!
目录
1、实现思路
编辑
2、各种接口的实现
2.1 初始化通讯录
2.2 添加通讯录数据
2.3 删除通讯录数据
2.4 展示通讯录数据
2.5 查找通讯录数据
2.6 修改通讯录数据
2.7 销毁通讯录数据
2.8 制作一个简易的菜单
3、完整源码
SeqList.h
Contact.h
SeqList.c
Contact.c
test.c
4、 完结散花
1、实现思路
前言:在我之前的文章当中已经写过一篇关于如何实现顺序表的文章(点击这里),而这篇文章主要是讲解如何基于顺序表实现通讯管理系统,所以要看明白这篇文章的宝子们一定要先看完写的顺序表的文章哦~
1.1 首先我们知道顺序表的底层就是数组,而通讯录就是顺序表通过封装过后的产物~
所以我们可以说通讯录的底层其实就是顺序表啦~
1.2我们知道之前实现顺序表的时候,每个数组中存放的是内置类型int,而我们如果想要将各种联系人的数据存放并管理起来,那么我们就必须先自定义一个结构体类型来存放每个联系人的数据。然后将每个联系人的数据存放到数组中统计管理起来~
我们用宏定义来表示数组的大小方便以后的调整~(这些代码都是写在contact.h头文件当中)
#define NAME_MAX 20
#define GENDER_MAX 20
#define TELE_MAX 20
#define ADDR_MAX 200
//定义一个自定义类型来存放我们所要的联系人信息
typedef struct PersonInformation
{
char name[NAME_MAX];//名字
char gender[GENDER_MAX];//性别
int age;//年龄
char tele[TELE_MAX];//电话
char addr[ADDR_MAX];//住址
}PeoInfo;
1.3 我们知道顺序表是这样定义的~
//定义一个动态顺序表的结构体变量
typedef struct SeqList
{
SLDataType* arr;
size_t num;//记录有效数据的个数
size_t capacity;//该顺序表的容量大小
}SL;//将该结构体类型重命名为SL
我们在顺序表的头文件当中引入Contact.h头文件(因为我们要将顺序表中的每个元素改成我们自定义是类型)
1.4 接下来我们就让顺序表摇身一变成为通讯录!
//前置申明
typedef struct SeqList contact;
这里我只是给顺序表重新取了一个名字(contact)!
因为头文件之间不可以互相包含,所以我们不可以将SeqList.h中的SL进行重命名,只能通过前置声明。
到这里我们就可以对通讯录进行各种接口的实现了!
Contact.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#define NAME_MAX 20
#define GENDER_MAX 20
#define TELE_MAX 20
#define ADDR_MAX 200
//定义一个自定义类型来存放我们所要的联系人信息
typedef struct PersonInformation
{
char name[NAME_MAX];//名字
char gender[GENDER_MAX];//性别
int age;//年龄
char tele[TELE_MAX];//电话
char addr[ADDR_MAX];//住址
}PeoInfo;
//前置申明
typedef struct SeqList contact;
//初始化通讯录
void InitContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
SeqList.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once//避免头文件的多次包含
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Contact.h"//需要自定义的类型,所以引入contact头文件
typedef PeoInfo SLDataType;//便于类型的改动
//定义一个动态顺序表的结构体变量
typedef struct SeqList
{
SLDataType* arr;
size_t num;//记录有效数据的个数
size_t capacity;//该顺序表的容量大小
}SL;//将该结构体类型重命名为SL
//加入增删查改接口
//1. 顺序表初始化
void SeqListInit(SL* p);
//2. 检查顺序表容量是否已满
void CheckSeqList(SL* p);
//3. 顺序表的尾插
void SeqListPushBack(SL* p, SLDataType x);
//4. 顺序表的尾删
void SeqListPopBack(SL* p);
//5. 顺序表的头插
void SeqListPushFront(SL* p, SLDataType x);
//6. 顺序表的头删
void SeqListPopFront(SL* p);
//7. 顺序表在pos位置插入
void SeqListInsert(SL* p, size_t pos, SLDataType x);
//8. 顺序表在pos位置删除
void SeqListErase(SL* p, size_t pos);
//9. 顺序表的查找
int SeqListFind(SL* p, SLDataType x);//如果该数字存在则返回该数字的下标,否则返回-1
//10 顺序表的销毁
void SeqListDestory(SL* p);
//11. 顺序表的打印
void SeqListPrint(SL* p);
2、各种接口的实现
2.1 初始化通讯录
因为我们希望在初始化通讯录时可以将我们之前保存的数据加载到通讯中,所以我们可以通过文件操作将数据加载到通讯录当中~
//导入原文件中的数据
void DoadContact(contact* con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo info = { 0 };
while (fread(&info, sizeof(PeoInfo), 1, pf))
{
SeqListPushBack(con, info);
}
printf("历史数据导入成功!\n");
fclose(pf);
pf = NULL;
}
在初始化时,我们就可以调用顺序表中的初始化接口来实现通讯录的初始化 ,然后再导入原数据~
//初始化通讯录
void InitContact(contact* con)
{
SeqListInit(con);
DoadContact(con);
}
2.2 添加通讯录数据
添加数据,我们就是在尾插的基础上对顺序表进行封装(头插也可以)!
//添加通讯录数据
void AddContact(contact* con)
{
PeoInfo p;
printf("请输入您要添加的联系人的姓名!\n");
scanf("%s", p.name);
printf("请输入您要添加的联系人的性别!\n");
scanf("%s", p.gender);
printf("请输入您要添加的联系人的年龄!\n");
scanf("%d", &(p.age));
printf("请输入您要添加的联系人的电话!\n");
scanf("%s", p.tele);
printf("请输入您要添加的联系人的住址!\n");
scanf("%s", p.addr);
SeqListPushBack(con, p);
printf("插入成功\n");
}
2.3 删除通讯录数据
这里我们通过名字来删除通讯录的数据!(当然我们也可以通过其他方式来删除数据)
//通过名字来查找通讯录中是否存在该联系人数据
int FindByName(contact* con,char* name)
{
for (int i = 0; i < con->num; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
return i;//如果找到就返回他的下标
}
}
return -1;//如果没找到返回一个无效的下标
}
//删除通讯录数据
void DelContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要删除的联系人的名字!\n");
scanf("%s", name);
int pos=FindByName(con, name);
if (pos < 0)
{
printf("您要删除的联系人不存在!\n");
return;
}
//调用顺序表中的在指定位置删除数据的函数
SeqListErase(con, pos);
printf("删除成功!\n");
}
2.4 展示通讯录数据
//展示通讯录数据
void ShowContact(contact* con)
{
printf("名字\t\t性别\t\t年龄\t\t电话\t\t\t住址\n");
for (int i = 0; i < con->num; i++)
{
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tele,
con->arr[i].addr
);
}
}
2.5 查找通讯录数据
//查找通讯录数据
void FindContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要查找的联系人的名字!\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("您要查找的联系人不存在!\n");
return;
}
printf("名字\t性别\t年龄\t电话\t住址\n");
printf("%s\t%s\t%d\t%s\t%s\t\n",
con->arr[pos].name,
con->arr[pos].gender,
con->arr[pos].age,
con->arr[pos].tele,
con->arr[pos].addr
);
}
2.6 修改通讯录数据
//修改通讯录数据
void ModifyContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要修改的联系人的名字!\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("您要修改的联系人不存在!\n");
return;
}
printf("请输入新的名字\n");
scanf("%s", con->arr[pos].name);
printf("请输入新的性别\n");
scanf("%s", con->arr[pos].gender);
printf("请输入新的年龄\n");
scanf("%d", &con->arr[pos].age);
printf("请输入新的电话\n");
scanf("%s", con->arr[pos].tele);
printf("请输入新的住址\n");
scanf("%s", con->arr[pos].addr);
printf("修改成功!\n");
}
2.7 销毁通讯录数据
在退出通讯录时我们希望将我们对通讯录进行的操作保存下来!
//将输入的数据保存到通讯录中
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo info = { 0 };
for (int i = 0; i < con->num; i++)
{
fwrite(con->arr + i, sizeof(PeoInfo), 1, pf);
}
printf("历史数据保存成功!\n");
fclose(pf);
pf = NULL;
}
//先保存数据到文件中再销毁通讯录数据
void DestroyContact(contact* con)
{
SaveContact(con);
//调用顺序表的销毁函数即可
SeqListDestory(con);
}
2.8 制作一个简易的菜单
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
#include"contact.h"
void menu()
{
printf("**********************************\n");
printf("*****1、增加用户 2、删除用户 ****\n");
printf("*****3、查找用户 4、修改用户 ****\n");
printf("*****5、展示用户 0、退出通讯录****\n");
printf("**********************************\n");
}
int main()
{
contact con = { 0 };
InitContact(&con);
int input = 0;
do
{
menu();
printf("请输入您的选择!\n");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
FindContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 0:
printf("退出通讯录成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while(input);
DestroyContact(&con);
return 0;
}
3、完整源码
SeqList.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once//避免头文件的多次包含
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"Contact.h"//需要自定义的类型,所以引入contact头文件
typedef PeoInfo SLDataType;//便于类型的改动
//定义一个动态顺序表的结构体变量
typedef struct SeqList
{
SLDataType* arr;
size_t num;//记录有效数据的个数
size_t capacity;//该顺序表的容量大小
}SL;//将该结构体类型重命名为SL
//加入增删查改接口
//1. 顺序表初始化
void SeqListInit(SL* p);
//2. 检查顺序表容量是否已满
void CheckSeqList(SL* p);
//3. 顺序表的尾插
void SeqListPushBack(SL* p, SLDataType x);
//4. 顺序表的尾删
void SeqListPopBack(SL* p);
//5. 顺序表的头插
void SeqListPushFront(SL* p, SLDataType x);
//6. 顺序表的头删
void SeqListPopFront(SL* p);
//7. 顺序表在pos位置插入
void SeqListInsert(SL* p, size_t pos, SLDataType x);
//8. 顺序表在pos位置删除
void SeqListErase(SL* p, size_t pos);
//9. 顺序表的查找
int SeqListFind(SL* p, SLDataType x);//如果该数字存在则返回该数字的下标,否则返回-1
//10 顺序表的销毁
void SeqListDestory(SL* p);
//11. 顺序表的打印
void SeqListPrint(SL* p);
Contact.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#define NAME_MAX 20
#define GENDER_MAX 20
#define TELE_MAX 20
#define ADDR_MAX 200
//定义一个自定义类型来存放我们所要的联系人信息
typedef struct PersonInformation
{
char name[NAME_MAX];//名字
char gender[GENDER_MAX];//性别
int age;//年龄
char tele[TELE_MAX];//电话
char addr[ADDR_MAX];//住址
}PeoInfo;
//前置申明
typedef struct SeqList contact;
//初始化通讯录
void InitContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
SeqList.c
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
//1. 顺序表初始化
void SeqListInit(SL* p)
{
assert(p);//判断指针的有效性
p->arr = NULL;
p->capacity = 0;
p->num = 0;
}
//2. 检查顺序表容量是否已满
void CheckSeqList(SL* p)
{
assert(p);//判断指针的有效性
if (p->num == p->capacity)
{
size_t newcapacity = p->capacity == 0 ? p->capacity = 4 : p->capacity * 2;
//如果原来没有空间,就给上4,有的话就扩大为原来的两倍
SLDataType* ptr = (SLDataType*)realloc(p->arr, newcapacity * sizeof(SLDataType));//动态扩容
if (ptr == NULL)
{
perror("realloc fail;");
return;
}
//也可以用assert断言一下
p->arr = ptr;//开辟成功将地址传给arr
p->capacity = newcapacity;//更新容量
}
}
//3. 顺序表的尾插
void SeqListPushBack(SL* p, SLDataType x)
{
assert(p);//判断指针的有效性
CheckSeqList(p);//尾插前先判断有没有容量或容量够不够
p->arr[p->num] = x;//尾部插入数据
p->num++;//有效数加一
}
//4. 顺序表的尾删
void SeqListPopBack(SL* p)
{
assert(p);//判断指针的有效性
assert(p->num > 0);//断言存在有效数据
p->num--;//尾删一个数据
}
//5. 顺序表的头插
void SeqListPushFront(SL* p, SLDataType x)
{
assert(p);//判断指针的有效性
CheckSeqList(p);//先判断容量是否满了
size_t end = p->num;
while (end)
{
p->arr[end] = p->arr[end - 1];//整体向后移动
end--;
}
p->arr[0] = x;//头插
p->num++;//有效数据加一
}
//6. 顺序表的头删
void SeqListPopFront(SL* p)
{
assert(p);//判断指针的有效性
assert(p->num > 0);//有数据才删除
size_t begin = 1;
while (begin < p->num)
{
p->arr[begin - 1] = p->arr[begin];//整体向前移动
begin++;
}
p->num--;// 有效数据减一
}
//7. 顺序表在pos位置插入
void SeqListInsert(SL* p, size_t pos, SLDataType x)
{
assert(p);//判断指针的有效性
assert(pos >= 0 && pos < p->num);//pos必须小于num并且大于等于0
CheckSeqList(p);//判断容量是否满了
for (int i = p->num; i > pos - 1; i--)
{
p->arr[i] = p->arr[i - 1];//将pos后面的元素往后挪
}
p->arr[pos - 1] = x;//在pos位置加入数据
p->num++;//有效个数加一
}
//8. 顺序表在pos位置删除
void SeqListErase(SL* p, size_t pos)
{
assert(p);//判断指针的有效性
assert(pos >= 0 && pos < p->num);//pos必须小于num并且大于等于0
assert(p->num > 0);//有数据才能删除
for (int i = pos; i < p->num-1; i++)
{
p->arr[i] = p->arr[i+1];//将pos后面的元素往后挪
}
p->num--;//有效个数减一
}
//9. 顺序表的查找
int SeqListFind(SL* p, SLDataType x)//如果该数字存在则返回该数字的下标,否则返回-1
{
assert(p);//断言
for (int i = 0; i < p->num; i++)
{
if (p->arr[i] == x)
{
return i;//查到返回下标
}
}
return -1;//没有查到
}
//10 顺序表的销毁
void SeqListDestory(SL* p)
{
assert(p);//判断指针的有效性
free(p->arr);//释放动态内存开辟的空间
p->arr = NULL;
p->capacity = 0;//容量置为0
p->num = 0;//有效个数置为0
}
//11. 顺序表的打印
void SeqListPrint(SL* p)
{
assert(p);//判断指针的有效性
if (p->num == 0)
{
printf("顺序表为空!\n");
return;
}
for (int i = 0; i < p->num; i++)
{
printf("%d ", p->arr[i]);//打印数据
}
printf("\n");
}
Contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
#include"Contact.h"
//导入原文件中的数据
void DoadContact(contact* con)
{
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo info = { 0 };
while (fread(&info, sizeof(PeoInfo), 1, pf))
{
SeqListPushBack(con, info);
}
printf("历史数据导入成功!\n");
fclose(pf);
pf = NULL;
}
//初始化通讯录
void InitContact(contact* con)
{
SeqListInit(con);
DoadContact(con);
}
//添加通讯录数据
void AddContact(contact* con)
{
PeoInfo p;
printf("请输入您要添加的联系人的姓名!\n");
scanf("%s", p.name);
printf("请输入您要添加的联系人的性别!\n");
scanf("%s", p.gender);
printf("请输入您要添加的联系人的年龄!\n");
scanf("%d", &(p.age));
printf("请输入您要添加的联系人的电话!\n");
scanf("%s", p.tele);
printf("请输入您要添加的联系人的住址!\n");
scanf("%s", p.addr);
SeqListPushBack(con, p);
printf("插入成功\n");
}
//通过名字来查找通讯录中是否存在该联系人数据
int FindByName(contact* con,char* name)
{
for (int i = 0; i < con->num; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
return i;//如果找到就返回他的下标
}
}
return -1;//如果没找到返回一个无效的下标
}
//删除通讯录数据
void DelContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要删除的联系人的名字!\n");
scanf("%s", name);
int pos=FindByName(con, name);
if (pos < 0)
{
printf("您要删除的联系人不存在!\n");
return;
}
//调用顺序表中的在指定位置删除数据的函数
SeqListErase(con, pos);
printf("删除成功!\n");
}
//展示通讯录数据
void ShowContact(contact* con)
{
printf("名字\t\t性别\t\t年龄\t\t电话\t\t\t住址\n");
for (int i = 0; i < con->num; i++)
{
printf("%s\t\t%s\t\t%d\t\t%s\t\t%s\n",
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tele,
con->arr[i].addr
);
}
}
//查找通讯录数据
void FindContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要查找的联系人的名字!\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("您要查找的联系人不存在!\n");
return;
}
printf("名字\t性别\t年龄\t电话\t住址\n");
printf("%s\t%s\t%d\t%s\t%s\t\n",
con->arr[pos].name,
con->arr[pos].gender,
con->arr[pos].age,
con->arr[pos].tele,
con->arr[pos].addr
);
}
//修改通讯录数据
void ModifyContact(contact* con)
{
char name[NAME_MAX];
printf("请输入您要修改的联系人的名字!\n");
scanf("%s", name);
int pos = FindByName(con, name);
if (pos < 0)
{
printf("您要修改的联系人不存在!\n");
return;
}
printf("请输入新的名字\n");
scanf("%s", con->arr[pos].name);
printf("请输入新的性别\n");
scanf("%s", con->arr[pos].gender);
printf("请输入新的年龄\n");
scanf("%d", &con->arr[pos].age);
printf("请输入新的电话\n");
scanf("%s", con->arr[pos].tele);
printf("请输入新的住址\n");
scanf("%s", con->arr[pos].addr);
printf("修改成功!\n");
}
//将输入的数据保存到通讯录中
void SaveContact(contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL)
{
perror("open fail\n");
return;
}
PeoInfo info = { 0 };
for (int i = 0; i < con->num; i++)
{
fwrite(con->arr + i, sizeof(PeoInfo), 1, pf);
}
printf("历史数据保存成功!\n");
fclose(pf);
pf = NULL;
}
//先保存数据到文件中再销毁通讯录数据
void DestroyContact(contact* con)
{
SaveContact(con);
//调用顺序表的销毁函数即可
SeqListDestory(con);
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"
#include"contact.h"
void menu()
{
printf("**********************************\n");
printf("*****1、增加用户 2、删除用户 ****\n");
printf("*****3、查找用户 4、修改用户 ****\n");
printf("*****5、展示用户 0、退出通讯录****\n");
printf("**********************************\n");
}
int main()
{
contact con = { 0 };
InitContact(&con);
int input = 0;
do
{
menu();
printf("请输入您的选择!\n");
scanf("%d", &input);
switch (input)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
FindContact(&con);
break;
case 4:
ModifyContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 0:
printf("退出通讯录成功!\n");
break;
default:
printf("输入错误,请重新选择!\n");
break;
}
} while(input);
DestroyContact(&con);
return 0;
}
4、 完结散花
好了,这期的分享到这里就结束了~
如果这篇博客对你有帮助的话,可以用你们的小手指点一个免费的赞并收藏起来哟~
如果期待博主下期内容的话,可以点点关注,避免找不到我了呢~
我们下期不见不散~~