前言:本篇文章主要是利用顺序表作为底层, 实现一个通讯录。偏向于应用, 对于已经学习过c++的友友们可能没有难度了已经。没有学习过c++的友友, 如果顺序表不会写, 或者说没有自己实现过, 请移步学习顺序表相关内容。 本节不会带领友友们再造一个轮子, 我们会直接使用现成的顺序表, 也就是我在这篇文章中实现的版本:顺序表知识点——顺序表的增删查改-CSDN博客
创建文件
先建立好文件:
我们要基于顺序表实现一个通讯录, 所以我们要拿出我们的顺序表, 也就是顺序表的.h和.c文件。
其次我们要建立一个通讯录的.h文件和一个通讯录.c文件。
其中通讯录的.h文件用来定义联系人自定义类型的结构体, 以及声明函数接口,.c文件则用来实现函数的接口。
定义联系人结构体
定义结构体, 我们要知道我们的联系人的成员变量应该有什么。
首先, 联系人一定要有姓名;其次, 联系人要有性别和电话。 而且, 我们可以加上一个地址,还有年龄。 那么基本的联系人的结构体里面的成员变量我们就考虑清楚了。 现在我们来进行定义结构体
#define NAME_MAX 20//最大的名字长度
#define GENDER_MAX 10//最大的性别长度
#define ADRESS_MAX 30//最大的地址长度
#define TEL_MAX 12//电话的长度是11, 最后一个留给字符零
/// .h
//性别姓名, 年龄地址
typedef struct personInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char adress[ADRESS_MAX];
}PInfo;
//以上, 就是我们定义的结构体
准备工作
定义好我们的结构体之后, 我们就可以做一些准备工作:主要是将我们的顺序表中的存储的数据改为我们自定义的联系人类型。 这里面会有函数的改动, 因为如果改变存储类型, 有些函数接口的操作就不好操作了。
第一步:先将我们的存储类型改为联系人类型:
打开我们的顺序表的头文件, 现在就可以发现, 我们之前实现的顺序表结构的优越性。 我们只要将红框框位置的int改为联系人类型, 那么这个顺序表存储的类型就改变了。 这样极大的降低了我们的维护成本。当然,改成联系人类型之前需要包含以下我们通讯录的头文件。
改完之后就是这样的:
第二步:我们要在我们的通讯录的头文件里面先写好要实现的接口。主要就是增删查改。
在写接口时, 要注意, 我们进行通讯录的增删查改的时候, 都是我们自己从键盘向流中输入数据, 而不是从内存中读取数据。 所以像插入操作,我们就不需要给一个要插入的参数了。
#define NAME_MAX 20
#define GENDER_MAX 10
#define ADRESS_MAX 30
#define TEL_MAX 12
/// .h
//性别姓名, 年龄地址
typedef struct personInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char adress[ADRESS_MAX];
}PInfo;
//结构体重定义加声明
typedef struct SeqList Contact;
//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDes(Contact* con);
//通讯录的增加
void ContactPush(Contact* con);
//通讯录的删除
void ContactErase(Contact* con);
//通讯录查找
void ContactFind(Contact* con);
//通讯录全展示
void ContactShow(Contact* con);
//通讯录修改联系人
void ContactModify(Contact* con);
这里要注意的是红框框这个重定义:
这个重定义的本质是 声明 + 重定义。这样写算是略写, 原本的写法应该是这样的:
struct SeqList;//先声明顺序表。
typedef struct SeqList Contact;//再对顺序表进行重定义。
注意, 为什么要声明顺序表?因为我们想要对顺序表进行重定义, 重定义成Contact, 也就是通讯录。 但是如果不包含顺序表的头文件, 或者声明以下顺序表, 编译器就不认识它。 那么这个时候能做的就是两种操作:一个是声明以下顺序表, 一个是包含以下顺序表的头文件。
但是我们往上看, 我们在上面已经在顺序表的头文件里包含了通讯录的头文件, 现在如果又将顺序表的头文件包含在通讯录中就会有一个重复包含的问题。 所以, 包含是行不通的,这里只能进行声明。
然后就是检查函数接口:检查顺序表的函数接口。 要知道, 我们之前的顺序表的函数接口都是按照存储的值是整形的方式来的。 那肯定是不行的。 不过检查函数接口不需要我们自己检查, 可以让编译器帮我们检查。
编译一下, 看看哪个接口报错, 直接把哪个接口注释掉就好了
经过编译器检查, 这里只有一个查找的函数接口报错了, 问题原因是我们自定义的类型无法进行 == 运算符的操作。 (这里很明显了, 在c语言中, 自定义类型一般是无法使用操作符的。但是c++可以, c++中有运算符重载, 可以令自定义类型使用操作符。)
把这一个接口注释掉。
然后就不报错了。
最后一个操作就是我们的大框架, 大框架还是我们那几个老几样: 菜单 + 输入 + 开关 + 循环。 如图:
#include"SeqList.h"
#include"Contact.h"
void menu()
{
printf("*************************************************\n");
printf("********* 1、Add *******\n");
printf("********* 2、erase *******\n");
printf("********* 3、modify *******\n");
printf("********* 4、Show *******\n");
printf("********* 5、Find *******\n");
printf("********* 0、exit *******\n");
printf("*************************************************\n");
}
int main()
{
int input = 0;
Contact con;
do
{
menu();
printf("请输入你的选择\n");
scanf("%d", &input);
switch (input)
{
case 1 :
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 0:
printf("已退出");
break;
default:
printf("输入非法");
break;
}
} while (input);
return 0;
}
这些准备工作做好之后,就差函数接口的实现了。 其实框架已经做好了, 可以运行一下看一下效果:
接口实现
初始化
最重要的就是通讯录的初始化。
通讯录的初始化, 其实就是顺序表的初始化。 因为我们的通讯录就是顺序表。 知识名字被typedef了一下。
所以, 通讯录的初始化, 我们直接调用顺序表的初始化就可以,它其实就是套了一层壳。
//通讯录的初始化
void ContactInit(Contact* con)
{
SeqListInit(con);
}
销毁
同理, 通讯录的销毁也就是顺序表的销毁。 直接套一层壳:
//通讯录的销毁
void ContactDes(Contact* con)
{
SeqListDestory(con);
}
插入数据
插入数据其实本质上也是顺序表的插入。 但是我们不能直接使用顺序表的插入套壳了。 因为我们的通讯录需要自己输入数据。 而不是从内存中读数据。
他们两个的接口就差一个参数
//通讯录的增加
void ContactPush(Contact* con);
//顺序表尾插函数接口
void SeqListPushback(SQL* ps, SQDataType x);
所以, 这里我们需要先实例化一个联系人对象。 再给这个对象赋值。 然后将这个对象传给顺序表的尾插接口:
//通讯录的增加
void ContactPush(Contact* con)
{
PInfo Inpo;//实例化联系人对象
//对这个对象进行赋值
printf("请输入你要添加的联系人姓名:>");
scanf("%s", Inpo.name);
printf("请输入你要添加的联系人性别:>");
scanf("%s", Inpo.gender);
printf("请输入你要添加的联系人年龄:>");
scanf("%d", &Inpo.age);
printf("请输入你要添加的联系人电话:>");
scanf("%s", Inpo.tel);
printf("请输入你要添加的联系人地址:>");
scanf("%s", Inpo.adress);
//添加数据, 插入
SeqListPushback(con, Inpo);
}
删除
删除数据的前提是我们要删除哪个数据。 这涉及到了查找。 比如要删除姓名叫”张三“的数据。 后者删除手机号是"…………"的数据。这里涉及到了查找。 所以我这里先创建一个查找接口。 这个查找是利用姓名查找。
这个接口的key, 也就是姓名,可以从外面传进来。 也可以再内部处理。 这里我选择再外面传进来。
//查找返回下标
int BynameIndex(Contact* con, char* name)
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(name, con->data[i].name) == 0)
{
return i;
}
}
return -1;
}
然后进行删除就可以了, 同样, 既然知道了下标。删除也是调用顺序表的删除操作。
//通讯录的删除
void ContactErase(Contact* con)
{
char name[NAME_MAX];
printf("请输入你要删除联系人的姓名:>\n");
scanf("%s", name);
int find = BynameIndex(con, name);
if (find >= 0)
{
SeqListPop(con, find);
}
else
{
printf("没有该联系人!\n");
return;
}
}
查找
查找在上面已经实现过了。 只需要将要查找的信息打印一下就好了
//查找通讯录的数据
void ContactFind(Contact* con)
{
char name[NAME_MAX];
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
int find = BynameIndex(con, name);
if (find >= 0)
{
printf("%s\t%s\t%d\t%s\t%s\n",
con->data[find].name,
con->data[find].gender,
con->data[find].age,
con->data[find].tel,
con->data[find].adress);
}
else
{
printf("没有该联系人!\n");
return;
}
}
修改
同理, 查找。 先确认要修改的联系人姓名, 然后再进行修改。 这里也涉及到了查找的问题。
void ContactModify(Contact* con)
{
char name[NAME_MAX];
printf("请输入你要查找联系人的姓名:>");
scanf("%s", name);
int find = BynameIndex(con, name);
if (find >= 0)
{
printf("请输入你要修改的联系人姓名:>");
scanf("%s", con->data[find].name);
printf("请输入你要修改的联系人性别:>");
scanf("%s", con->data[find].gender);
printf("请输入你要修改的联系人年龄:>");
scanf("%d", &con->data[find].age);
printf("请输入你要修改的联系人电话:>");
scanf("%s", con->data[find].tel);
printf("请输入你要修改的联系人地址:>");
scanf("%s", con->data[find].adress);
}
else
{
printf("无联系人\n");
return;
}
全部联系人展示
全部展示就是只需要将顺序表中的每个数据的每个成员依次打印, 一个循环即可:
//通讯录全展示
void ContactShow(Contact* con)
{
for (int i = 0; i < con->size; i++)
{
printf("%s\t%s\t%d\t%s\t%s\n",
con->data[i].name,
con->data[i].gender,
con->data[i].age,
con->data[i].tel,
con->data[i].adress);
}
}
以上, 就是顺序表实现通讯录的全部内容。 下面是我上传的本篇文章对应的源代码。想要的自行下载。