目录
前情提示:
在学习了顺序表的基础知识后,我们就可以基于顺序表的实现来制作一个简易的通讯录项目,来增强我们对顺序表的应用和理解。
在日常通讯录的使用中,我们通常会用到:创建通讯录、销毁通讯录、增加联系人、删除联系人、查找联系人、展示通讯录等六种操作。
因此,本篇博客将对这六种功能的实现做详细的讲解。
一:定义联系人
在上一篇顺序表的基本操作中,顺序表里储存的每个元素为整形。
而在通讯录里,里面储存的每一个元素都是一个联系人。
一个联系人里面又包含许多信息,如姓名、性别、年龄、电话、住址等。
因此我们可以用一个结构体来定义联系人,并且放在contact.h中进行声明。
contact.h
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 12
#define ADDR_MAX 100
typedef struct PersonInfo
{
char name[NAME_MAX];//姓名
char sex[SEX_MAX];//性别
int age;//年龄
char tel[TEL_MAX];//电话
char add[ADDR_MAX];//住址
}People;把结构体重命名为People
并且通过#define对结构体内字符串的长度做出了限制。
二:顺序表是通讯录的底层
通讯录的实现是基于顺序表的基本操作实现的,因此我们要先把顺序表的创建(初始化)、销毁、添加、删除、查找、打印的相关操作实现出来。
先用头文件声明顺序表和顺序表的基本实现形式。
SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include"contact.h"
typedef People SLDataType;//定义新的数据类型
#define CAPACITY 4
typedef struct Seqlist
{
SLDataType* arr;
int size; //有效数据个数
int capacity; //数组容量
} SL;
//初始化顺序表
void SLInit(SL* pa);
//销毁顺序表
void SLDel(SL* pa);
//顺序表空间检查和扩容
void SLCheakCapacity(SL* pa);
//顺序表尾部插入数据
void SLPushBack(SL* pa, SLDataType x);//x为插入的数据
//顺序表头部插入数据
void SLPushHead(SL* pa, SLDataType x);
//顺序表尾部删除数据
void SLDelBack(SL* pa);
//顺序表头部删除数据
void SLDelHead(SL* pa);
//顺序表指定位置插入数据
void SLSpecifyInsert(SL* pa, int pos, SLDataType x);//pos为插入的位置
//顺序表指定位置删除数据
void SLErase(SL* pa, int pos);
注意:我们在"SeqList.h"头文件中调用了头文件"contact.h",并且把"contact.h"中的People结构体重命名为了SLDataType类型,也是由于现在顺序表中存储的都为联系人类型的数据。
接下来就是顺序表基本操作的实现,我们在上一章已经讲解过了,这里就直接上代码。
SeqList.c
//顺序表初始化
void SLInit(SL* pa)
{
assert(pa);
pa->arr = (SLDataType*)malloc(sizeof(SLDataType) * CAPACITY);开辟动态数组
if (pa->arr==NULL)
{
perror("初始化失败");
return 1;
}
pa->size = 0;
pa->capacity = CAPACITY;
}
//销毁顺序表
void SLDel(SL* pa)
{
assert(pa);
free(pa->arr);//释放数组空间
pa->arr = NULL;
pa->size = pa->capacity =0;
}
//顺序表空间检查和扩容
void SLCheakCapacity(SL* pa)
{
assert(pa);
if (pa->size == pa->capacity)
{
SLDataType *pb;
pb = (SLDataType*)realloc(pa->arr, sizeof(SLDataType) * pa->capacity * 2);//扩容
if (pb == NULL)
{
perror("扩容失败");
return 1;
}
free(pa->arr);
pa->arr = pb;
pa->capacity *= 2;
}
}
//顺序表尾部插入数据
void SLPushBack(SL* pa, SLDataType x)
{
assert(pa);
SLCheakCapacity(pa);//扩容
pa->arr[pa->size] = x;
pa->size++;
}
//顺序表头部插入数据
void SLPushHead(SL* pa, SLDataType x)
{
SLCheakCapacity(pa);
for (int i = pa->size; i > 0; i--)
{
pa->arr[i] = pa->arr[i - 1];
}
pa->arr[0] = x;
pa->size++;
}
//顺序表尾部删除数据
void SLDelBack(SL* pa)
{
assert(pa);
pa->size--;
}
//顺序表头部删除数据
void SLDelHead(SL* pa)
{
assert(pa);
for (int i =0; i <pa->size-1; i++)
{
pa->arr[i] = pa->arr[i + 1];
}
pa->size--;
}
//顺序表指定位置插入数据
void SLSpecifyInsert(SL* pa, int pos, SLDataType x)
{
SLCheakCapacity(pa);
for (int i = pos; i<pa->size; i--)
{
pa->arr[i+1] = pa->arr[i];
}
pa->arr[pos] = x;
pa->size++;
}
//顺序表指定位置删除数据
void SLErase(SL* pa, int pos)
{
assert(pa);
for (int i = pos; pos < pa->size-1; i++)
{
pa->arr[i] = pa->arr[i + 1];
}
pa->size--;
}
顺序表的声明和实现完成,接下来就是基于顺序表的基本操作来实现通讯录项目。
三:通讯录的实现
通讯录基于顺序表,那么对通讯录进行操作其实就是对顺序表进行操作。
因此,在进行具体的实现操作之前,要在"contact.h"头文件中声明struct SeqList结构体,这样我们才可以在contact.h内使用struct SeqList*类型的参数。
这里有一个问题?
为什么不直接声明SL,而是声明struct SeqList?
这是因为contact.h头文件中并没有包含SeqList.h,因此不能使用重命名后的SL,因为重命名操作是在定义完struct SeqList结构体之后进行的,而我们只能声明最原始的struct SeqList。
然后,再typedef struct SeqList contact,其实就是换个名字,从原来的对顺序表结构体操作到现在对通讯录操作(实际还是对顺序表结构体操作)。
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 12
#define ADDR_MAX 100
typedef struct PersonInfo
{
char name[NAME_MAX];//姓名
char sex[SEX_MAX];//性别
int age;//年龄
char tel[TEL_MAX];//电话
char add[ADDR_MAX];//住址
}People;//结构体类型
struct Seqlist;//调用必须先声明
typedef struct Seqlist contact;
1.通讯录的初始化操作
通讯录的初始化其实就是对顺序表的初始化,因为初始化操作就是开辟空间并且将size置为0,capacity置为预期值(CAPACITY 4),并不涉及到对顺序表中联系人数据的改动。
代码展示:
contact.h
//初始化通讯录
void InitContact(contact* con);函数声明
contact.c
//初始化通讯录
void InitContact(contact* con)
{
assert(con);
SLInit(con);
}
test.c
contact con;
InitContact(&con);
printf("通讯录已创建\n");
2.通讯录的销毁操作
通讯录的销毁其实也是顺序表的销毁,道理与初始化一样。
代码展示:
contact.h
//销毁通讯录
void DelContact(contact* con);
contact.c
//销毁通讯录
void DelContact(contact* con)
{
assert(con);
SLDel(con);
}
test.c
contact con;
DelContact(&con);
printf("通讯录已销毁\n");
3.添加联系人
添加联系人,就是往顺序表里输入一个联系人类型(People类型)的数据。
因此我们要先创建一个People类型的变量,然后往变量里输入数据。
最后,再把变量传给SLPushBack函数,往顺序表中尾插。
代码示例:
contact.h
//添加联系人
void AddContact(contact* con);
contact.c
//添加联系人
void AddContact(contact* con)
{
assert(con);
People xiaoming;
//在添加联系人之前,我们要把联系人的数据准备好
printf("请输入要添加的联系人姓名:");
scanf("%s", &xiaoming.name); printf("\n");
printf("添加联系人性别:");
scanf("%s", &xiaoming.sex); printf("\n");
printf("添加联系人年龄:");
scanf("%d", &xiaoming.age); printf("\n");
printf("添加联系人电话:");
scanf("%s", &xiaoming.tel); printf("\n");
printf("添加联系人住址:");
scanf("%s", &xiaoming.add); printf("\n");
SLPushBack(con, xiaoming);
}
text.c
//添加联系人
contact con;
AddContact(&con);
printf("联系人已添加完成\n");
4.删除联系人
在通讯录中删除一个联系人,其实就是在顺序表中删除一个People类型的数据。
需要用到顺序表中删除指定位置数据的基本操作。
代码示例:
contact.h
//删除联系人
void ConDel(contact* con, int pos);
contact.c
/删除联系人
void ConDel(contact* con, int pos)
{
assert(con);
SLErase(con,pos);
}
test.c
int pos = 0;
printf("请输入你要删除的联系人序号\n");
scanf("%d", &pos);
ConDel(&con, pos);
printf("联系人已删除\n");
5.展示通讯录
展示通讯录本质就是把顺序表中存储的联系人数据一个个打印出来。
代码示例:
contact.h
//展示通讯录
void ShowContact(contact* con);
contact.c
//展示通讯录
void ShowContact(contact* con)
{
assert(con);
for (int i =0; i<con->size; i++)//打印所有有效数据
{
printf("xxxxx序号%dxxxxx\n", i);
printf("联系人姓名:%s\n", con->arr[i].name);
printf("联系人性别:%s\n", con->arr[i].sex);
printf("联系人年龄:%d\n",con->arr[i].age);
printf("联系人电话:%s\n", con->arr[i].tel);
printf("练习人住址:%s\n", con->arr[i].add);
printf("\n");
}
}
test.c
//展示通讯录
contact con;
ShowContact(&con);
6.查找联系人并打印联系人数据
查找联系人就是通过联系人名字或电话找到联系人,需要用到顺序表的查找操作可以做到,然后再把找到的这个联系人的下标传给打印函数把联系人数据打印出来。
代码示例:
contact.h
//查找联系人
int ConFind(contact* con, char* a);
//打印指定联系人
void SpecifyPrint(contact* con, int pos);
contact.c
//查找联系人
int ConFind(contact* con, char* a)
{
assert(con);
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, a)==0)//strcmp函数判断字符串是否相等,等于0则相等
{
printf("找到了");
return i;//返回查找到的联系人坐标
}
}
printf("没找到");
return -1;//没找到返回-1
}
//打印指定联系人
void SpecifyPrint(contact* con, int pos)
{
printf("%s\n", con->arr[pos].name);
printf("%s\n", con->arr[pos].sex);
printf("%d\n", con->arr[pos].age);
printf("%s\n", con->arr[pos].tel);
printf("%s\n", con->arr[pos].add);
}
test.c
char a[] = { 0 };
printf("请输入你要查找的联系人姓名\n");
scanf("%s", &a);
int ret=ConFind(&con,a);//查找联系人,找到则返回指定下标
if (ret>=0)
{
SpecifyPrint(&con, ret);//查找成功则打印联系人数据
}
至此,通讯录的各个实现步骤已经全部讲解完毕。
四:最终代码和成果展示
1.最终代码
循序表的全部代码上面已经写出,接下来只写contact的代码。
contact.h
#pragma once
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 12
#define ADDR_MAX 100
typedef struct PersonInfo
{
char name[NAME_MAX];//姓名
char sex[SEX_MAX];//性别
int age;//年龄
char tel[TEL_MAX];//电话
char add[ADDR_MAX];//住址
}People;//结构体类型
struct Seqlist;//调用必须先声明
typedef struct Seqlist contact;
//初始化通讯录
void InitContact(contact* con);
//销毁通讯录
void DelContact(contact* con);
//添加联系人
void AddContact(contact* con);
//展示通讯录
void ShowContact(contact* con);
//删除联系人
void ConDel(contact* con, int pos);
//查找联系人
int ConFind(contact* con, char* a);
//打印指定联系人
void SpecifyPrint(contact* con, int pos);
contact.c
#include"SeqList.h"
#include"contact.h"
//初始化通讯录
void InitContact(contact* con)
{
assert(con);
SLInit(con);
}
//销毁通讯录
void DelContact(contact* con)
{
assert(con);
SLDel(con);
}
//添加联系人
void AddContact(contact* con)
{
assert(con);
People xiaoming;
//在添加联系人之前,我们要把联系人的数据准备好
printf("请输入要添加的联系人姓名:");
scanf("%s", &xiaoming.name); printf("\n");
printf("添加联系人性别:");
scanf("%s", &xiaoming.sex); printf("\n");
printf("添加联系人年龄:");
scanf("%d", &xiaoming.age); printf("\n");
printf("添加联系人电话:");
scanf("%s", &xiaoming.tel); printf("\n");
printf("添加联系人住址:");
scanf("%s", &xiaoming.add); printf("\n");
SLPushBack(con, xiaoming);
}
//展示通讯录
void ShowContact(contact* con)
{
assert(con);
for (int i =0; i<con->size; i++)
{
printf("xxxxx序号%dxxxxx\n", i);
printf("联系人姓名:%s\n", con->arr[i].name);
printf("联系人性别:%s\n", con->arr[i].sex);
printf("联系人年龄:%d\n",con->arr[i].age);
printf("联系人电话:%s\n", con->arr[i].tel);
printf("练习人住址:%s\n", con->arr[i].add);
printf("\n");
}
}
//删除联系人
void ConDel(contact* con, int pos)
{
assert(con);
SLErase(con,pos);
}
//查找联系人
int ConFind(contact* con, char* a)
{
assert(con);
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, a)==0)
{
printf("找到了");
return i;
}
}
printf("没找到");
return -1;
}
//打印指定联系人
void SpecifyPrint(contact* con, int pos)
{
printf("%s\n", con->arr[pos].name);
printf("%s\n", con->arr[pos].sex);
printf("%d\n", con->arr[pos].age);
printf("%s\n", con->arr[pos].tel);
printf("%s\n", con->arr[pos].add);
}
test.c
#include"SeqList.h"
int main()
{
contact con;
//创建联系人目录
printf(" xxxxxxxxxxxxxxxxxxxxxxxxxxxxx \n");
printf(" 1.创建通讯录 2.销毁通讯录 \n");
printf(" 3.新增联系人 4.删除联系人 \n");
printf(" 5.查找联系人 6.展示通讯录 \n");
printf(" xxxxxxxxxxxxxxxxxxxxxxxxxxxxx \n");
int input;
do
{
scanf("%d", &input);
//switch语句来进行选择操作
switch (input)
{
case 1:
{
InitContact(&con);
printf("通讯录已创建\n");
break;
}
case 2:
{
DelContact(&con);
printf("通讯录已销毁\n");
break;
}
case 3:
{
AddContact(&con);
printf("联系人已添加完成\n");
break;
}
case 4:
{
int pos = 0;
printf("请输入你要删除的联系人序号\n");
scanf("%d", &pos);
ConDel(&con, pos);
printf("联系人已删除\n");
break;
}
case 5:
{
char a[] = { 0 };
printf("请输入你要查找的联系人姓名\n");
scanf("%s", &a);
int ret=ConFind(&con,a);
if (ret>=0)
{
SpecifyPrint(&con, ret);
}
break;
}
case 6:
{
ShowContact(&con);
break;
}
}
} while (input);
return 0;
}
2.成果展示
屏幕录制 2024-04-30 185840
感兴趣可以自己去尝试一下。
这就是本章博客的全部内容,如果内容对你有所帮助,不妨留个赞支持一下!谢谢!
完结撒花。