目录
前言
大家好鸭!这里是小鸥~
在上篇博客小鸥带领大家实现了一个顺序表,那么我们就趁热打铁,实现一个基于顺序表的通讯录项目吧!(完整代码会附带在文章末尾)
通讯录前的准备
在创建通讯录之前,由于本次通讯录基于动态顺序表实现,所以现附上上篇博客中的顺序表,具体实现请参考上篇博客——《数据结构の顺序表》
顺序表代码
具体实现见上一篇《数据结构の顺序表》
SeqList.c文件(函数实现):
#include "SeqList.h"
//初始化
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//销毁
void SLDestroy(SL* ps)
{
if(ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
打印
//void SLPrint(SL* ps)
//{
// for (int i = 0; i < ps->size; i++)
// {
// printf("%d ", ps->arr[i]);
// }
// fputs("\n", stdout);
//}
//扩容
void SLCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = (ps->capacity == 0 ? 4 : 2 * ps->capacity);
SLDateType* tmp = (SLDateType*)realloc(ps->arr, newcapacity * sizeof(SLDateType));
if (tmp == NULL)//动态内存开辟失败
{
perror("realloc fail!");
exit(1);
}
//动态内存开辟成功
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
//尾插
void SLPushBack(SL* ps, SLDateType x)
{
assert(ps);
//扩容
SLCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
//头插
void SLPushFront(SL* ps, SLDateType x)
{
assert(ps);
//申请空间
SLCheckCapacity(ps);
//将已有数据往后移一位
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
//将数据插入到第一位
ps->arr[0] = x;
//有效数据++
ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
ps->size--;
}
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//数据往前移动一位
for (int i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
//有效数据--
ps->size--;
}
//指定位置之前插入(下标位置)
void SLInsert(SL* ps, int pos, SLDateType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);//防止指定位置pos越界
//申请空间
SLCheckCapacity(ps);
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
//指定位置删除数据
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
查找
//int SLFind(SL* ps,SLDateType x)
//{
// assert(ps);
// for (int i = 0; i < ps->size; i++)
// {
// if (ps->arr[i] == x)
// return i;//找到后返回下标
// }
// return -1;//没找到返回一个不可能的下标
//}
SeqList.h文件(包含函数):
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Contact.h"
//typedef int SLDateType;
typedef peoInfo SLDateType;//将数据类型改为联系人结构体
//顺序表的创建
typedef struct SeqList
{
SLDateType* arr;
int size;//有效数据个数
int capacity;//空间容量
}SL;
//初始化
void SLInit(SL* ps);
//销毁
void SLDestroy(SL* ps);
//打印
void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//尾插
void SLPushBack(SL* ps, SLDateType x);
//头插
void SLPushFront(SL* ps, SLDateType x);
//尾删
void SLPopBack(SL* ps);
//头删
void SLPopFront(SL* ps);
//指定位置之前插入(下标位置)
void SLInsert(SL* ps, int pos, SLDateType x);
//指定位置之前删除
void SLErase(SL* ps, int pos);
//查找
int SLFind(SL* ps, SLDateType x);
注:顺序表代码中注释的查找和打印函数,由于在通讯录中,顺序表储存的数据类型改变,所以不再适用,因此注释掉。SeqList.h文件中包含的Contact.h文件就是下文中要创建的通讯录的头文件。
理解通讯录与顺序表的关系
1.本次通讯录实现,是在顺序表基础上实现的,就是在顺序表的基础上,将动态顺序表中数组成员存储的数据类型改为本次通讯录需要的“联系人”结构体数据类型
2.通讯录在数据的增、删、查、改上都是基于顺序表中的函数实现的,是在顺序表的基础上,由于数据类型改变,做出了进一步的包装,本篇通讯录本质还是一个顺序表。
通讯录Contact.h文件
1.创建联系人信息结构体(数据类型)
在顺序表的总结篇中,我们演示用的数据类型是int,顺序表所存储的数据类型是可以自己定义的,今天的通讯录要使用的“联系人”数据类型就是使用结构体创建的自定义类型(包含在Contact.h头文件中)。
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100
//通讯录信息,联系人结构体
//姓名 性别 年龄 电话 地址
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}peoInfo;
本篇演示的通讯录联系人信息为姓名,性别,年龄,电话以及住址五个信息(可以自行添加其他需要的信息),再将联系人结构体重命名为peoInfo方便使用。
在定义成员数组的大小时,最好使用#define定义常量,这样更便于修改。
2.完整代码:
#pragma once
#define NAME_MAX 20
#define GENDER_MAX 10
#define TEL_MAX 20
#define ADDR_MAX 100
//通讯录信息,联系人结构体
//姓名 性别 年龄 电话 地址
typedef struct PersonInfo
{
char name[NAME_MAX];
char gender[GENDER_MAX];
int age;
char tel[TEL_MAX];
char addr[ADDR_MAX];
}peoInfo;
//将顺序表重命名为通讯录方便理解
typedef struct SeqList Contact;
//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDesTroy(Contact* con);
//通讯录添加数据
void ContactAdd(Contact* con);
//通讯录删除数据
void ContactDel(Contact* con);
//展示通讯录数据
void ContactShow(Contact* con);
//通讯录的修改
void ContactModify(Contact* con);
//联系人查找并展示其信息
void ContactFind(Contact* con);
此处将顺序表重命名为Contact(通讯录)方便后文理解。
后文Contact.c文件中的函数实现将按照头文件中函数顺序来实现。
通讯录Contact.c文件
1.通讯录的初始化
由于通讯录的本质就是顺序表,所以初始化和顺序表是一样的,直接调用顺序表的初始化函数即可。
#include "Contact.h"
#include "SeqList.h"
//通讯录的初始化
void ContactInit(Contact* con)
{
//实际上就是顺序表的初始化
//直接调用顺序表的初始化函数就行
SLInit(con);
}
2.通讯录的销毁
也和初始化一样直接调用顺序表的销毁函数即可
//通讯录的销毁
void ContactDesTroy(Contact* con)
{
//和初始化一样,直接调用顺序表的销毁函数即可
SLDestroy(con);
}
3.通讯录添加数据
添加数据函数,由于此时顺序表中数组的数据类型是我们自己定义的“联系人”结构体,所以先创建一个新结构体info,将用户要添加的信息存储到这个结构体中,此时一个新的联系人也就创建完成了,再调用顺序表的尾插函数,将本次数据插入到顺序表中即可。
//通讯录添加数据
void ContactAdd(Contact* con)
{
peoInfo info;
printf("请输入要添加的联系人姓名:\n");
scanf("%s", info.name);
printf("请输入要添加的联系人性别:\n");
scanf("%s", info.gender);
printf("请输入要添加的联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入要添加的联系人电话:\n");
scanf("%s", info.tel);
printf("请输入要添加的联系人住址:\n");
scanf("%s", info.addr);
//使用顺序表的插入数据函数将信息加入到通讯录中
SLPushBack(con,info);
}
总结步骤:
1.创建一个联系人结构体;
2.引导用户输入联系人信息到结构体中;
3.将本次数据插入到顺序表中。
4.通讯录删除数据
由于在删除数据时要先知道本次想要删除的数据,即联系人是哪个,所以要先确定通讯录中是否存在该联系人,若存在,再进行下一步的删除操作,而查找一个联系人的是否存在的依据也不止一个,本次演示基于姓名的查找方式:
//联系人查找(姓名)
int FindByName(Contact* con, char name[])
{
assert(con);
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)//使用strcmp函数比较字符串是否相同
return i;//找到返回下标位置
}
return -1;
}
1.当查找到该姓名存在时,说明该联系人存在,返回该联系人在通讯录中的位置(即顺序表数组成员中元素的下标位置) ;若不存在,则返回一个不可能的下标位置-1。
2.该查找函数只在这些.c文件中的函数内部使用,在使用时不会直接调用,所以没有在Contact.h中声明。
有了查找到联系人的方法之后,我们再实现删除操作的函数:
//通讯录删除数据
void ContactDel(Contact* con)
{
//输入要删除的联系人姓名
char name[NAME_MAX];
printf("请输入要删除的联系人姓名:\n");
scanf("%s", &name);
//在通讯录中查找是否存在该信息
int find = FindByName(con, name);
if (find < 0)
{
printf("要删除的联系人数据不存在!\n");
return;
}
SLErase(con, find);
printf("删除联系人成功!\n");
}
总结步骤:
1.用户输入要删除的联系人的姓名;
2.依据输入姓名调用查找函数,找到目标联系人的下标位置;
3.调用顺序表的指定位置删除函数,将该联系人信息从通讯录中删除。
5.展示通讯录数据
即打印出现有通讯录的所有联系人信息;由于创建通讯录时存储的数据类型改变,所以上篇中顺序表以int类型为数据类型时的,打印顺序表的函数已经不再适用(SeqList.c中已经注释),此处为全新的函数。
//展示通讯录数据
void ContactShow(Contact* con)
{
//打印表头
printf("%s %s %s %s %s\n","姓名","性别","年龄","电话","地址");
//遍历通讯录,按格式打印联系人信息
for (int i = 0; i < con->size; i++)
{
printf("%s %s %d %s %s\n", \
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr);
}
}
总结步骤:
1.先打印表头,以表示信息类别;
2.使用结构体指针访问结构体成员,并按格式打印出来(此处没有调整表头与数据的对齐,可自行调整需要的占位空间)。
6.通讯录的修改
修改通讯录中已经有的联系人信息,顺序表中没有这个功能的函数,且根据不同的数据类型,修改的方式可能不同,所以这里为针对联系人结构体数据类型的新函数。
//通讯录的修改
void ContactModify(Contact* con)
{
//输入要修改的联系人姓名
char name[NAME_MAX];
printf("请输入要修改的联系人姓名;\n");
scanf("%s", &name);
//通讯录中查找要修改的联系人
int find = FindByName(con, name);
if (find < 0)
{
printf("要修改的联系人数据不存在!\n");
return;
}
printf("请输入新的姓名:\n");
scanf("%s", con->arr[find].name);
printf("请输入新的性别:\n");
scanf("%s", con->arr[find].gender);
printf("请输入新的年龄:\n");
scanf("%d", &con->arr[find].age);
printf("请输入新的电话:\n");
scanf("%s", con->arr[find].tel);
printf("请输入新的地址:\n");
scanf("%s", con->arr[find].addr);
printf("修改成功!\n");
}
总结步骤:
1.输入要修改的联系人的姓名;
2.使用FindByName来查找该联系人的位置;
3.修改该位置上的联系人信息。
7.查找联系人并展示其信息
此处的查找函数不同于上文中查找联系人下标的FindByName函数,是给使用者使用的,查找是否存在,且打印目标信息的查找函数,所以要在Contact.h头文件中声明。
由于此查找函数的查找依据也是name,所以查找的过程可以直接调用上文的FindByName函数
//联系人查找并展示其信息
void ContactFind(Contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的联系人姓名:\n");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
printf("要查找的联系人数据不存在!\n");
return;
}
//打印表头
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
//按格式打印联系人信息
printf("%s %s %d %s %s\n", \
con->arr[find].name,
con->arr[find].gender,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr);
}
总结步骤:
1.输入要查找的联系人姓名;
2.调用查找函数(使用其他成员数据如:gender、age、tel、addr时的查找函数请自行模仿FindByName函数来实现);
3.输出表头和目标联系人的相关信息。
使用通讯录
当完善完成通讯录的各种功能后,我们创建一个test.c文件来测试其功能:
#include "SeqList.h"
//通讯录使用
void menu()
{
printf("********************************************\n");
printf("****** ******\n");
printf("**** 1.添加联系人 2.删除联系人 ****\n");
printf("** 3.修改联系人 4.查找联系人 **\n");
printf("**** 5.展示通讯录 0.退出通讯录 ****\n");
printf("****** ******\n");
printf("********************************************\n");
}
int main()
{
int Input = -1;
Contact con;
ContactInit(&con);//初始化
do
{
menu();
printf("请选择你的操作:\n");
scanf("%d", &Input);
switch(op)
{
case 1:
ContactAdd(&con);
break;
case 2:
ContactDel(&con);
break;
case 3:
ContactModify(&con);
break;
case 4:
ContactFind(&con);
break;
case 5:
ContactShow(&con);
break;
case 0:
printf("退出通讯录...\n");
break;
default:
printf("输入错误,请重新输入您的操作!\n");
}
} while (Input != 0);
return 0;
}
要点:
1.创建一个通讯录,并初始化(不初始化将导致错误);
2.使用printf创建一个简易的菜单界面,将功能展示出来,使用switch语句来调用各个功能的函数
使用过程这里就不再演示了,亲各位读者亲自去动手试一试吧!
其实还可以加上文件管路的内容,做到将输入的联系人信息储存到文件中长期保存起来,不过这里就不做示范了,有兴趣的话大家可以讨论一下评论出来哦!
后记
本篇的通讯录的创建到这里也就结束啦!大家可以自己去试着敲一敲,动起手来,理解上会更加的深刻哦!
当然,如果有不足的地方欢迎大家在评论区友好的指出,大家互相进步O!
这里是小鸥,咱们下期不见不散~~
附完整Contact.c代码:
#include "Contact.h"
#include "SeqList.h"
//通讯录的初始化
void ContactInit(Contact* con)
{
//实际上就是顺序表的初始化
//直接调用顺序表的初始化函数就行
SLInit(con);
}
//通讯录的销毁
void ContactDesTroy(Contact* con)
{
//和初始化一样,直接调用顺序表的销毁函数即可
SLDestroy(con);
}
//通讯录添加数据
void ContactAdd(Contact* con)
{
peoInfo info;
printf("请输入要添加的联系人姓名:\n");
scanf("%s", info.name);
printf("请输入要添加的联系人性别:\n");
scanf("%s", info.gender);
printf("请输入要添加的联系人年龄:\n");
scanf("%d", &info.age);
printf("请输入要添加的联系人电话:\n");
scanf("%s", info.tel);
printf("请输入要添加的联系人住址:\n");
scanf("%s", info.addr);
//使用顺序表的插入数据函数将信息加入到通讯录中
SLPushBack(con,info);
}
//联系人查找(姓名)
int FindByName(Contact* con, char name[])
{
assert(con);
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)//使用strcmp函数比较字符串是否相同
return i;//找到返回下标位置
}
return -1;
}
//通讯录删除数据
void ContactDel(Contact* con)
{
//输入要删除的联系人姓名
char name[NAME_MAX];
printf("请输入要删除的联系人姓名:\n");
scanf("%s", &name);
//在通讯录中查找是否存在该信息
int find = FindByName(con, name);
if (find < 0)
{
printf("要删除的联系人数据不存在!\n");
return;
}
SLErase(con, find);
printf("删除联系人成功!\n");
}
//展示通讯录数据
void ContactShow(Contact* con)
{
//打印表头
printf("%s %s %s %s %s\n","姓名","性别","年龄","电话","地址");
//遍历通讯录,按格式打印联系人信息
for (int i = 0; i < con->size; i++)
{
printf("%s %s %d %s %s\n", \
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr);
}
}
//通讯录的修改
void ContactModify(Contact* con)
{
//输入要修改的联系人姓名
char name[NAME_MAX];
printf("请输入要修改的联系人姓名;\n");
scanf("%s", &name);
//通讯录中查找要修改的联系人
int find = FindByName(con, name);
if (find < 0)
{
printf("要修改的联系人数据不存在!\n");
return;
}
printf("请输入新的姓名:\n");
scanf("%s", con->arr[find].name);
printf("请输入新的性别:\n");
scanf("%s", con->arr[find].gender);
printf("请输入新的年龄:\n");
scanf("%d", &con->arr[find].age);
printf("请输入新的电话:\n");
scanf("%s", con->arr[find].tel);
printf("请输入新的地址:\n");
scanf("%s", con->arr[find].addr);
printf("修改成功!\n");
}
//联系人查找并展示其信息
void ContactFind(Contact* con)
{
char name[NAME_MAX];
printf("请输入要查找的联系人姓名:\n");
scanf("%s", &name);
int find = FindByName(con, name);
if (find < 0)
{
printf("要查找的联系人数据不存在!\n");
return;
}
//打印表头
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
//按格式打印联系人信息
printf("%s %s %d %s %s\n", \
con->arr[find].name,
con->arr[find].gender,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr);
}