文章目录
顺序表思想实现通讯录
实现通讯录前,我们考虑一下,通讯录需要包含什么内容?
联系人,联系人需要包含姓名年龄电话性别这3种基本信息。
我们知道顺序表实质是个数组,如果我们让数组的每个元素都代表一个联系人,每个联系人又需要包含多条信息,所以,我们想到结构体,让数组的每个元素都是结构体,结构体就能很好地描述每一个联系人。
对于这样一个通讯录,我们采用顺序表的思想,但不单独写顺序表的接口,直接实现通讯录的接口,从无到有。
头文件
头文件的书写主要注意以下事项:
- 结构体的声明
- 命名的可读性,便于理解
- 需要哪些对通讯录操作,声明出对应函数
//Contact.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#define MAX_NAME 20
#define MAX_GENDER 10
#define MAX_TELNUM 20
typedef struct personinfo
{
char name[MAX_NAME];
size_t age;
char telnum[MAX_TELNUM];
char gender[MAX_GENDER];
}peoinfo;
typedef struct Contact
{
peoinfo* a;
int size;
int capacity;
}Contact;
//初始化通讯录
void ContactInit(Contact* con);
//添加联系人
void ContactAdd(Contact* con);
//删除联系人
void ContactDel(Contact* con);
//修改联系人
void ContactModify(Contact* con);
//展示通讯录
void ContactShow(Contact* con);
//查找联系人信息
void ContactFind(Contact* con);
//销毁通讯录
void ContactDestory(Contact* con);
接口函数
注意:
- 函数实现要包含必要的语句说明
- 函数主体部分为顺序表的增删查改
//Contact.c
# define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void ContactInit(Contact* con)
{
assert(con);
con->a = NULL;
con->size = 0;
con->capacity = 0;
}
void ContactAdd(Contact* con)
{
assert(con);
peoinfo new;
printf("请输入新联系人的姓名:\n");
scanf("%s", new.name);
printf("请输入新联系人的年龄:\n");
scanf("%zd", &new.age);
printf("请输入新联系人的电话:\n");
scanf("%s", new.telnum);
printf("请输入新联系人的性别:\n");
scanf("%s", new.gender);
if (con->size == con->capacity)
{
int newcapacity = con->capacity == 0 ? 4 : con->capacity * 2;
peoinfo* tmp = (peoinfo*)realloc(con->a, sizeof(peoinfo) * newcapacity);
if (NULL == tmp)
{
printf("add error!REASON: realloc function failed!\n");
exit(-1);
}
con->a = tmp;
con->capacity = newcapacity;
}
con->a[con->size] = new;
con->size++;
}
int FindIndex(Contact* con, char* obj)
{
for (int i = 0; i < con->size; i++)
{
if (0 == strcmp(con->a[i].name, obj))
return i;
}
return -1;
}
void ContactDel(Contact* con)
{
assert(con);
char del[MAX_NAME] = { 0 };
printf("请输入要删除的联系人的姓名:\n");
scanf("%s", del);
int ret = FindIndex(con, del);
if (ret < 0)
{
printf("您要删除的联系人不存在!\n");
return;
}
else
{
for (int i = ret; i < con->size - 1; i++)
{
con->a[i] = con->a[i + 1];
}
con->size--;
printf("删除成功!\n");
}
}
void ContactModify(Contact* con)
{
assert(con);
char mod[MAX_NAME] = { 0 };
printf("请输入要修改的联系人的姓名:\n");
scanf("%s", mod);
int ret = FindIndex(con, mod);
if (ret < 0)
{
printf("您要修改的联系人不存在!\n");
return;
}
else
{
char newmod[MAX_NAME] = { 0 };
size_t newage = 0;
char newtelnum[MAX_TELNUM] = { 0 };
char newgender[MAX_TELNUM] = { 0 };
printf("请输入新的联系人姓名:\n");
scanf("%s", con->a[ret].name);
printf("请输入新的联系人年龄:\n");
scanf("%zd", &con->a[ret].age);
printf("请输入新的联系人电话:\n");
scanf("%s", con->a[ret].telnum);
printf("请输入新的联系人性别:\n");
scanf("%s", con->a[ret].gender);
printf("修改成功!\n");
}
}
void ContactShow(Contact* con)
{
assert(con);
if (con->size == 0)
{
printf("通讯录为空!\n");
return;
}
else
{
printf("----------------------------------------------------------------------------------\n");
printf("%-11s %-11s %-11s %-11s\n", "姓名", "年龄", "电话", "性别");
for (int i = 0; i < con->size; i++)
{
printf("%-11s %-11zd %-11s %-11s\n", con->a[i].name, con->a[i].age, con->a[i].telnum, con->a[i].gender);
}
printf("----------------------------------------------------------------------------------\n");
}
}
void ContactFind(Contact* con)
{
assert(con);
char find[MAX_NAME] = { 0 };
printf("请输入要查询的联系人的姓名:\n");
scanf("%s", find);
int ret = FindIndex(con, find);
if (ret < 0)
{
printf("您要查询的联系人不存在!\n");
return;
}
else
{
printf("查询结果如下:\n");
printf("%-11s %-11s %-11s %-11s\n", "姓名", "年龄", "电话", "性别");
printf("%-11s %-11zd %-11s %-11s\n", con->a[ret].name, con->a[ret].age, con->a[ret].telnum, con->a[ret].gender);
}
}
void ContactDestory(Contact* con)
{
assert(con);
con->size = con->capacity = 0;
free(con->a);
con->a = NULL;
}
主函数
主函数里也是包含一些函数定义的,包括菜单、加载文件、写入文件。
为了持久化保留数据,我们需要调用我们之前学习过的文件知识,将每次更新的数据写入文件中,每次启动程序前载入历史数据,每次程序终止前写入文件,这样就完成了通讯录。
关于文件知识,小裤儿之前发布过一篇文件管理的博客,介绍了文件管理函数等知识。
//interface.c
# define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
void menu()
{
printf("******************通讯录*******************\n");
printf("*******1.添加联系人 2.删除联系人*******\n");
printf("*******3.修改联系人 4.查询联系人*******\n");
printf("*******5.展示通讯录 0.退出通讯录*******\n");
printf("*******************************************\n");
}
void SaveContact(Contact* con)
{
FILE* pf = fopen("contact.txt", "wb");
if (NULL == pf)
{
perror("fopen");
return;
}
for (int i = 0; i < con->size; i++)
{
fwrite(con->a + i, sizeof(peoinfo), 1, pf);
}
fclose(pf);
pf = NULL;
printf("通讯录数据保存成功!\n");
}
void LoadContact(Contact* con)
{
FILE* pf = fopen("contact.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return;
}
peoinfo buff;
while (fread(&buff, sizeof(peoinfo), 1, pf))
{
if (con->size == con->capacity)
{
int newcapacity = con->capacity == 0 ? 4 : con->capacity * 2;
peoinfo* tmp = (peoinfo*)realloc(con->a, sizeof(peoinfo) * newcapacity);
if (NULL == tmp)
{
printf("add error!REASON: realloc function failed!\n");
exit(-1);
}
con->a = tmp;
con->capacity = newcapacity;
}
con->a[con->size] = buff;
con->size++;
}
fclose(pf);
pf = NULL;
printf("历史数据读取成功!\n");
}
int main()
{
Contact con;
ContactInit(&con);
LoadContact(&con);
int input = 0;
do
{
menu();
printf("请输入你想执行的操作:\n");
scanf("%d", &input);
switch (input)
{
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 (input);
SaveContact(&con);
ContactDestory(&con);
return 0;
}
学好了顺序表后,通讯录的实现较为简单,代码很容易看懂,如果代码实现有难度,可能是顺序表并没有完全理解或者是知识点不够熟练,如果有问题,我很荣幸能在评论区和你一起讨论。
效果如下:
如果觉得界面稍乱,可以通过增加换行、分隔线或清理屏幕解决。