前言
本篇章笔者将会带领学者写一个 顺序表 的实践项目 - 通讯录 。 相信通过本篇章学者将会对顺序表有更深的理解。
一、通讯录的介绍
提到通讯录大家肯定会非常熟悉了 , 但要说实现可能还是有一定的难度的。这里我们弄清楚一个问题
● 为什么用顺序表实现通讯录 ?
首先,顺序表的底层其实就是数组 , 然而通讯录就是要做到 “ ‘增’ ‘删’ ‘查’ ‘改’ ” , 也要很快随意的访问联系人 , 这样来看 : 顺序表就占有很大优势了。
● 实现怎样的通讯录 ?
我们实现的是简易的通讯录项目 ,主要是练习顺序表部分的知识 , 想要实现可视化的通讯录列表还需更多知识的支撑,这里笔者就不介绍了 . 其简易通讯录包括 : 通讯录查找联系人信息 , 通讯录添加联系人信息 , 通讯录删除联系人信息 , 通讯录修改联系人信息 。
● 项目要求
◐ 至少能够存储100个人的通讯信息
◐ 能够保存用户信息:名字、性别、年龄、电话、地址等
◐ 增加联系人信息
◐ 删除指定联系人
◐ 查找制定联系人
◐ 修改指定联系人
◐ 显示联系人信息人
二、相关接口
实现的是项目 , 所以项目中还是分为三个文件 : 实现文件(.c) 、 声明文件(.h) 、 测试文件(.c) 。
因为顺序表要实现增删查改的操作 , 要做到 " 按需申请 "动态顺序表就显得更合适了.
● 初始化通讯录
● 添加通讯录联系人信息
● 删除通讯录联系人信息
● 查找通讯录联系人信息
● 修改通讯录联系人信息
● 展示通讯录联系人信息
● 销毁通讯录联系人信息
三、通讯录的实现分析
首先, 通讯录是集结了好多好友的很多信息 , 一个信息就会与相应的一个人有关, 我们通过查找联系人的姓名,电话等就可以查到该联系人. 那要实现该怎么实现呢?
顺序表底层是数组 ,数组中每个元素就可以充当一个联系人 ,之前笔者讲到过顺序表中元素可以是自定义元素,所以联系人的信息我们可以定义在结构体中让其组成顺序表,这样就形成了通讯录.
▶ 图例:
以上图是我们考虑的重点 , 顺序表中每个元素的类型是我们自定义的 " 联系人信息 " 类型 ,然而每一个联系人的信息都具有相同的结构 , 这样我们可以考虑定义结构体来封装联系人信息 .
▶ 联系人信息
联系人信息笔者给出以下几个:
◐ 姓名
◐ 电话
◐ 年龄
◐ 家庭住址
◐ 性别
学者可自行添加 !
四、通讯录的实现
因为我们底层使用顺序表实现通讯录 , 所以要先实现顺序表 , 然后调用接口即可.
※关于前置声明
◐ 什么是前置声明?
前置声明就是在一个文件中声明另一个文件中的内容 ,声明一个类或结构体而不定义它。这种方式是让编译器知道该类型或结构体的存在。
◐ 顺序表实现用到的前置声明
▶ 顺序表的声明
#include "Contacts.h"
//顺序表的声明
typedef PersonInfo SqDataType;
typedef struct SeqList
{
SqDataType* arr;
int size; // 有效元素个数
int capacity;// 空间大小
}STList;
以上是顺序表实现的声明部分,其中 PersonInfo 是顺序表中数据的类型 , 但在 Contacts 的声明文件中编译器是找不到 PersonInfo 的 , 所以要包含对应头文件才可以让编译器找到该声明并使用。 故:本声明文件中需要包含 — > Contacts.h
▶ 通讯录的声明(错位演示)
#include "SeqList.h"
#define NAME_MAX 100
#define PHONE_MAX 30
#define HOME_ADDRRSS_MAX 200
#define SEX_MAX 4
//通讯录的声明
typedef struct PersonInfo
{
char name[NAME_MAX];
char phone[PHONE_MAX];
int age;
char Home_Address[HOME_ADDRRSS_MAX];
char sex[SEX_MAX];
}PersonInfo;
//初始化通讯录
void InitContact(SeqList* con);
以上为通讯录的声明 , 虽然我们实现的通讯录底层是顺序表,但当我们实现 初始化通讯录时编译器是不认识 SeqList 的, 这时需要包含头文件才可以让编译器知道。 但是在顺序表的声明文件中(SeqList.h)中已经包含了 " Contacts.h " 这样的头文件,若我们在通讯录的声明文件中再次包含 " SeqList.h "这样的头文件 ,就会存在头文件包重的问题 , 这是 C 语言不支持的!
◐ 头文件互包错误结果
▶ 通讯录的声明(正确代码)
//通讯录的声明
//定义联系人数据类型
#define NAME_MAX 100
#define PHONE_MAX 30
#define HOME_ADDRRSS_MAX 200
#define SEX_MAX 4
typedef struct PersonInfo
{
char name[NAME_MAX];
char phone[PHONE_MAX];
int age;
char Home_Address[HOME_ADDRRSS_MAX];
char sex[SEX_MAX];
}PersonInfo;
//**************前置声明 *****************
typedef struct SqList Contacts;
※正确实现代码
● 顺序表的声明文件 ( SeqList.h )
#pragma once
#include "Contacts.h"
//顺序表的声明
typedef PersonInfo SqDataType;
typedef struct SeqList
{
SqDataType* arr;
int size; // 有效元素个数
int capacity;// 空间大小
}STList;
//初始化
void SeqListInit(STList * psl);
//销毁
void SeqListDesTroy(STList* psl);
//尾插
void SeqListPushBack(STList* psl, SqDataType x);
//指定位置删除
void SeqListPop(STList* psl , int pos);
● 顺序表的实现文件 ( SeqList.c )
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
//初始化
void SeqListInit(STList* psl)
{
psl->arr = NULL;
psl->size = psl->capacity = 0;
}
//销毁
void SeqListDesTroy(STList* psl)
{
if (psl->arr)
{
free(psl->arr);
}
psl->arr = NULL;
psl->size = psl->capacity = 0;
}
void Chick_capacity(STList* chick)
{
if (chick->capacity == chick->size)
{
//申请空间 - 动态增容
int Newcapacity = chick->capacity == 0 ? 4 : 2*chick->capacity;
SqDataType* tmp = (SqDataType*)realloc(chick->arr, Newcapacity * sizeof(SqDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
//申请成功
chick->arr = tmp;
chick->capacity = Newcapacity;
}
}
//尾插
void SeqListPushBack(STList* psl, SqDataType x)
{
assert(psl);
//检查空间容量
Chick_capacity(psl);
psl->arr[psl->size++] = x;
}
//指定位置删除
void SeqListPop(STList* psl , int pos)
{
//pos 之后的数据 , 后面移动到前面
for (int i = pos+1; i < psl->size ; i++)
{
//后面数据移动到前面
psl->arr[i-1] = psl->arr[i];
}
--psl->size;
}
● 通讯录的声明文件 ( Contacts.h )
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <windows.h>
//通讯录的声明
//定义联系人数据类型
#define NAME_MAX 100
#define PHONE_MAX 30
#define HOME_ADDRRSS_MAX 200
#define SEX_MAX 4
typedef struct PersonInfo
{
char name[NAME_MAX];
char phone[PHONE_MAX];
int age;
char Home_Address[HOME_ADDRRSS_MAX];
char sex[SEX_MAX];
}PersonInfo;
//前置声明
typedef struct SeqList Contacts;
//通讯录接口
//初始化通讯录
void InitContact(Contacts* con);
//添加通讯录数据
void AddContact(Contacts* con);
//删除通讯录数据
void DelContact(Contacts* con);
//展示通讯录数据
void ShowContact(Contacts* con);
//查找通讯录数据
void FindContact(Contacts* con);
//修改通讯录数据
void ModifyContact(Contacts* con);
//销毁通讯录数据
void DestroyContact(Contacts* con);
● 通讯录的实现文件( Contacts.c )
#define _CRT_SECURE_NO_WARNINGS
#include "Contacts.h"
#include "SeqList.h"
//初始化通讯录
void InitContact(Contacts* con)
{
SeqListInit(con);
}
//添加通讯录数据
void AddContact(Contacts* con)
{
//创建联系人
PersonInfo Info;
//用户选择
printf("请输入您要添加的联系人姓名:\n");
scanf("%s",Info.name);
printf("请输入您要添加的联系人电话:\n");
scanf("%s", Info.phone);
printf("请输入您要添加的联系人年龄:\n");
scanf("%d", &Info.age);
printf("请输入您要添加的联系人家庭住址:\n");
scanf("%s", Info.Home_Address);
printf("请输入您要添加的联系人性别:\n");
scanf("%s", Info.sex);
//写入联系人信息表中 -- 尾插到顺序表
SeqListPushBack(con , Info);
}
//查找联系人姓名
int Find_Name(Contacts* con , char* name)
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))
{
//找到了
return i;
}
}
//没有找到
return -1;
}
//删除通讯录数据
void DelContact(Contacts* con)
{
char name[NAME_MAX];
printf("请输入您要删除的联系人姓名:");
scanf("%s", name);
printf("\n");
printf("正在删除,请稍后......\n");
Sleep(1000);
int find = Find_Name(con, name);
if (find >= 0)
{
SeqListPop(con , find);
printf("删除成功!\n");
ShowContact(con);
}
else
{
printf("******您删除的联系人不存在, 请您先添加该联系人!******\n");
printf("\n");
}
}
//展示通讯录数据
void ShowContact(Contacts* con)
{
printf("以下是联系人簿信息:\n");
printf("\n");
//遍历通讯录,打印每个联系人信息
for (int i = 0; i < con->size; i++)
{
printf("姓名: %2s\n电话: %2s\n年龄:%6d\n家庭住址:%2s\n性别: %2s\n",
con->arr[i].name,
con->arr[i].phone,
con->arr[i].age,
con->arr[i].Home_Address,
con->arr[i].sex);
printf("\n");
}
printf("\n");
}
//查找通讯录数据
void FindContact(Contacts* con)
{
char name[NAME_MAX];
printf("请输入您要查找的联系人姓名:");
scanf("%s", name);
printf("\n");
printf("正在查找中,请稍后.....\n");
Sleep(1000);
int flag = 0; // 1 代表找到
for (int i = 0; i < con->size; i++)
{
if ( 0 == strcmp(con->arr[i].name, name))
{
printf("该联系人已找到\n");
ShowContact(con);
flag = 1;
}
}
if (flag == 0)
{
printf("******您查找的联系人不存在,请您先添加该联系人******\n");
printf("\n");
}
}
//修改姓名
void Modify_name(Contacts* con , int find)
{
printf("请输入新的姓名: \n");
scanf("%s", con->arr[find].name);
}
//修改电话
void Modify_phone(Contacts* con, int find)
{
printf("请输入新的电话: \n");
scanf("%s", con->arr[find].phone);
}
//修改年龄
void Modify_age(Contacts* con, int find)
{
printf("请输入新的年龄: \n");
scanf("%d", &con->arr[find].age);
}
//修改家庭住址
void Modify_home_address(Contacts* con, int find)
{
printf("请输入新的家庭住址: \n");
scanf("%s", con->arr[find].Home_Address);
}
//修改性别
void Modify_sex(Contacts* con, int find)
{
printf("请输入新的性别: \n");
scanf("%s", con->arr[find].sex);
}
//修改通讯录数据
void ModifyContact(Contacts* con)
{
char name[NAME_MAX];
printf("请您先输入修改的联系人姓名:\n");
scanf("%s", name);
printf("\n");
printf("正在查找中,请稍后.....\n");
Sleep(1000);
printf("\n");
printf("\n");
int find = Find_Name(con, name);
if (find < 0)
{
printf("******您修改的联系人不存在, 请您先添加该联系人!******\n");
printf("\n");
}
else
{
printf("您修改的联系人存在!\n");
printf("\n");
printf("以下是修改信息:\n");
printf("*** 0.结束选择 1. 姓名 2.电话 3.年龄 4.家庭住址 5.性别 ***\n");
int chance = 0;
do
{
printf("请您对该联系人的信息修改做出选择:\n");
scanf("%d", &chance);
switch (chance)
{
case 0:
printf("结束选择...\n");
break;
case 1:
Modify_name(con, find);
break;
case 2:
Modify_phone(con, find);
break;
case 3:
Modify_age(con, find);
break;
case 4:
Modify_home_address(con, find);
break;
case 5:
Modify_sex(con, find);
break;
default:
printf("您输入有误,请重新选择!\n");
printf("\n");
}
}while(chance);
}
}
//销毁通讯录数据
void DestroyContact(Contacts* con)
{
SeqListDesTroy(con);
}
● 测试文件 ( Test.c )
#define _CRT_SECURE_NO_WARNINGS
#include "Contacts.h"
#include "SeqList.h"
void menu()
{
printf("******************通讯录******************\n");
printf("*******1.添加联系人 2.删除联系人********\n");
printf("*******3.修改联系人 4.查找联系人********\n");
printf("*******5.展示联系人 0. 退出 *********\n");
printf("******************************************\n");
}
int main()
{
int op = -1;
Contacts con;
InitContact(&con);
do {
menu();
printf("请选择您的操作:\n");
scanf("%d", &op);
//要根据对应的op执行不同的操作
switch (op)
{
case 1:
AddContact(&con);
break;
case 2:
DelContact(&con);
break;
case 3:
ModifyContact(&con);
break;
case 4:
FindContact(&con);
break;
case 5:
ShowContact(&con);
break;
case 0:
printf("退出通讯录....\n");
break;
default:
printf("输入错误,请重新选择您的操作!\n");
break;
}
} while (op != 0);
DestroyContact(&con);
}
※效果展示
总结
以上我们可以实现一个简易的通讯录项目 , 以上是笔者对于之前知识的回顾, 同时希望学者能够学完并独立的写出 , 这样相信顺序表部分就会熟悉的掌握了!