一、写在开始
顺序表写到这里,难免会好奇,这个顺序表有啥用?
这里,我们可以用顺序表写一个通讯录。
基础要求:我将要用到结构体、动态内存管理、顺序表、⽂件操作来实现通讯录的创造
功能:
我给他赋予了以下几个功能,并会教给大家怎样实现代码操作。
二、代码实现
1.原理
首先,我画一张图便于大家理解顺序表和通讯录有什么联系。
由图可见,一个顺序表是一个数组,一个数组对应的一个下标里面是一个结构体,每个结构体代表了一个通讯录里面一个人的信息,把他放在顺序表里面就是一个通讯录了。上一次我已经发过了一篇这样写顺序表的文章,这次的通讯录也离不开顺序表。
2.处理文件
- 我们需要contact.h记录通讯录的结构以及函数的定义声明
- 我们需要contact.c实现通讯录的函数
- 我们需要seqlist.h记录顺序表结构,定义声明顺序表函数
- 我们需要seqlist.c实现顺序表的函数
- 我们需要test.c测试代码
3.定义联系人数据结构
既然是有关于联系人的定义,即写在contct.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];
}perinfo;
4.定义类型数据
此定义数据类型,是需要用到顺序表的,即是将此定义在seqlist.h中。
typedef int sltype;//方便后续替换数组类型
在写顺序表的时候我为了方便替换数据类型写了一条这样的代码,为了方便测试数据,我写了一个int型数组。而现在,我们知道了通讯录在数组里面是一个结构体,于是我们就可以直接将int型替换成数组类型。
typedef struct personInfo sltype;
这里如果想要将struct personInfo写成perinfo则需要在seqlist.h里面包含一下contact.h文件。
5.给顺序表改名字
既然要写通讯录,就给顺序表改个名字吧。
//contact.h
typedef struct seqlist contact;
6.通讯录函数
文件录入
写一个代码录入历史联系人数据,这里会用到文件操作。
void LoadContact(Contact* con) {
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//循环读取⽂件数据
perinfo info;
while (fread(&info, sizeof(perinfo), 1, pf))
{
slpushback(con, info);
}
printf("历史数据已录入成功!\n");
}
要写函数先在contact.h里面把所有要写的函数都定义好
//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDesTroy(Contact* con);
//通讯录添加数据
void ContactAdd(Contact* con);
//通讯录删除数据
void ContactDel(Contact* con);
//通讯录的修改
void ContactModify(Contact* con);
//通讯录查找
void ContactFind(Contact* con);
//展示通讯录数据
void ContactShow(Contact* con);
函数都写在了contact.c文件里。
1.初始化通讯录
//contact.c
void ContactInit(contact* con) {
//实际上就是进行顺序表初始化
slinit(con);
LoadContact(con);
}
2.添加通讯录数据
//通讯录添加数据
void ContactAdd(Contact* con)
{
//获取用户输入的内容:姓名+性别+年龄+电话+地址
perinfo 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);
}
3.删除通讯录数据
先写一个姓名查找。
//姓名查找联系人
int FindByName(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))
{
//找到了
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");
}
4.展示通讯录数据
void ContactShow(Contact* con)
{
//表头:姓名 性别 年龄 电话 地址
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
//遍历通讯录,按照格式打印每个联系人数据
for (int i = 0; i < con->size; i++)
{
printf("%3s %3s %3d %3s %3s\n", //手动调整一下格式
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr
);
}
}
5.查找通讯录数据
//姓名查找联系人
int FindByName(Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))//这里记得添加头文件
{
//找到了
return i;
}
}
//没有找到
return -1;
}
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");
}
写完保存一下通讯录的数据,这里又要用到文件操作。
void SaveContact(Contact* con) {
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//将通讯录数据写⼊⽂件
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr + i, sizeof(perinfo), 1, pf);
}
printf("通讯录数据保存成功!\n");
}
7.销毁通讯录数据
void ContactDesTroy(Contact* con)
{
SaveContact(con);
sldestroy(con);
}
三、完整demo
//seqlist.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include"contact.h"
typedef perinfo sltype;
typedef struct seqlist
{
sltype* arr;
int size;
int capacity;
}sl;
//typedef struct seqlist sl;
//初始化和销毁
void slinit(sl* ps);
void sldestroy(sl* ps);
//扩容
void slCheckCapacity(sl* ps);
//尾部插入删除/头部插入删除
void slpushback(sl* ps, sltype x);
void slpopback(sl* ps);
void slpushfront(sl* ps, sltype x);
void slpopfront(sl* ps);
//指定位置之前插入/删除数据
void slinsert(sl* ps, int pos, sltype x);
void slerase(sl* ps, int pos);
//seqlist.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"seqlist.h"
void slinit(sl* ps) {
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//申请空间
void slCheckCapacity(sl* ps) {
if (ps->capacity == ps->size) {
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
sltype* tmp=(sltype*)realloc(ps->arr, newcapacity * sizeof(sltype));
if (tmp == NULL) {
perror("realloc");
exit(1);//直接退出,程序不再执行
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
void sldestroy(sl* ps) {
if (ps->arr) {
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
//尾部插入删除
void slpushback(sl* ps, sltype x) {
assert(ps);
slCheckCapacity(ps);
ps->arr[ps->size++] = x;
}
void slpopback(sl* ps) {
assert(ps);
//顺序表不能为空
assert(ps->size);
//只进行--操作不影响增删查改
--ps->size;
}
//头部插入删除
void slpushfront(sl* ps, sltype 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 slpopfront(sl* ps) {
assert(ps);
assert(ps->size);
int i;
for (i = 0; i < ps->size; i++) {
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//指定位置之前插入
void slinsert(sl* ps, int pos, sltype x) {
assert(ps);
assert(pos >= 0&&pos<=ps->size);
//插入数据,空间够不够
slCheckCapacity(ps);
//让pos及之后的数据整体往后挪
int i;
for (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);
int i;
for (i = pos; i < ps->size-1; i++) {
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//contact.h
#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];
}perinfo;
//给顺序表改个名字,叫做通讯录
typedef struct seqlist Contact; //sl
//通讯录的初始化
void ContactInit(Contact* con);
//通讯录的销毁
void ContactDesTroy(Contact* con);
//通讯录添加数据
void ContactAdd(Contact* con);
//通讯录删除数据
void ContactDel(Contact* con);
//通讯录的修改
void ContactModify(Contact* con);
//通讯录查找
void ContactFind(Contact* con);
//展示通讯录数据
void ContactShow(Contact* con);
//contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
#include"seqList.h"
void LoadContact(Contact* con) {
FILE* pf = fopen("contact.txt", "rb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//循环读取⽂件数据
perinfo info;
while (fread(&info, sizeof(perinfo), 1, pf))
{
slpushback(con, info);
}
printf("历史数据已录入成功!\n");
}
//通讯录的初始化
void ContactInit(Contact* con)//sl
{
//实际上要进行的是顺序表的初始化
//顺序表的初始化已经实现好了
slinit(con);
LoadContact(con);
}
//通讯录添加数据
void ContactAdd(Contact* con)
{
//获取用户输入的内容:姓名+性别+年龄+电话+地址
perinfo 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[])
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->arr[i].name, name))
{
//找到了
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 %3s %4d %-8s %6s\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)
{
//11
char name[NAME_MAX];
printf("请输入要查找的联系人姓名\n");
scanf("%s", name);
int find = FindByName(con, name);
if (find < 0)
{
printf("要查找的联系人数据不存在!\n");
return;
}
// 姓名 性别 年龄 电话 地址
// 11 11 11 11 11
printf("%s %s %s %s %s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%s %3s %4d %-8s %6s\n", //手动调整一下格式
con->arr[find].name,
con->arr[find].gender,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr
);
}
void SaveContact(Contact* con) {
FILE* pf = fopen("contact.txt", "wb");
if (pf == NULL) {
perror("fopen error!\n");
return;
}
//将通讯录数据写⼊⽂件
for (int i = 0; i < con->size; i++)
{
fwrite(con->arr + i, sizeof(perinfo), 1, pf);
}
printf("通讯录数据保存成功!\n");
}
//通讯录的销毁
void ContactDesTroy(Contact* con)
{
SaveContact(con);
sldestroy(con);
}
我们可以在测试代码里面写一个菜单,使得通讯录更加完整。
//text.c
#define _CRT_SECURE_NO_WARNINGS 1
#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;
Contact con;
ContactInit(&con);
do {
menu();
printf("请选择您的操作:\n");
scanf("%d", &op);
//要根据对应的op执行不同的操作
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");
break;
}
} while (op != 0);
ContactDesTroy(&con);
return 0;
}
四、小结
我写这篇文章,是为了让大家在学习了顺序表后并能运用它,顺序表作为数据结构的底层,是非常有必要学习的理解的,希望这篇文章对大家有所帮助。