1. 多文件操作
SeqList.h //用于定义顺序表的结构,增删查改等函数的声明
SeqList.c //用于实现增删查改等函数
Contact.h //用于定义通讯录的结构,通讯录中联系人的增删查改等函数的声明
Contact.c //用于实现通讯录中增删查改等函数
Test.h //用于测试上述实现的函数,和菜单界面
2. 通讯录的主要功能
1.初始化通讯录
2.销毁通讯录
3.展示联系人数据
4.增加联系人数据
5.判断指定联系人是否存在
6.删除联系人数据
7.查找联系人数据
8.修改联系人数据
对通讯录中各个功能的展示:
3. 通讯录的实现
想要基于顺序表实现通讯录,首先我们需要一个顺序表。由于顺序表的实现比较简单,这里直接给出,不进行详细说明 (如想要详细了解顺序表的实现,请前往我的主页查看)。
说明:下面两个文件中注释的部分是在顺序表类型替换后(由整型替换为结构体类型)的不适用的部分。读者不必在意。
SeqList.h
在这个头文件中由于要用上Contact.h中定义的peoInfo,所以要包含Contact.h。
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Contact.h"
//typedef int SQDataType;
typedef peoInfo SQDataType;
typedef struct SeqList
{
SQDataType* arr;
int size;
int capacity;
}SL;
void SLInit(SL* ps);
void SLDestory(SL* ps);
//void SLPrint(SL ps);
void SLPushBack(SL* ps, SQDataType x);
void SLPushFront(SL* ps, SQDataType x);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//void SLFind(const SL* ps, SQDataType x);
void SLInsert(SL* ps, int pos, SQDataType x);
void SLErase(SL* ps, int pos);
SeqList.c
#define _CRT_SECURE_NO_WARNINGS
#include "SeqList.h"
void SLInit(SL* ps)
{
ps->arr = (SQDataType*)malloc(sizeof(SQDataType) * 4);
if (ps->arr == NULL)
{
perror("malloc fail!\n");
return ;
}
ps->capacity = 4;
ps->size = 0;
}
void SLDestory(SL* ps)
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = 0;
ps->size = 0;
}
void CheckCapacity(SL* ps,SQDataType x)
{
if (ps->size == ps->capacity)
{
SQDataType* tmp = (SQDataType*)realloc(ps->arr, sizeof(SQDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail!\n");
return ;
}
else
{
ps->arr = tmp;
ps->capacity *= 2;
}
}
}
//void SLPrint(SL ps)
//{
// for (int i = 0; i < ps.size; i++)
// {
// printf("%d ", ps.arr[i]);
// }
// printf("\n");
//
//}
void SLPushBack(SL* ps, SQDataType x)
{
assert(ps);
CheckCapacity(ps, x);
ps->arr[ps->size] = x;
ps->size++;
}
void SLPushFront(SL* ps, SQDataType x)
{
assert(ps);
CheckCapacity(ps, x);
int i = ps->size;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[i] = x;
ps->size++;
}
void SLPopBack(SL* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("无数据可删除!\n");
return;
}
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
if (ps->size == 0)
{
printf("无数据可删除!\n");
return;
}
for (int i = 0; i < ps->size; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
//void SLFind(const SL* ps, SQDataType x)
//{
// assert(ps);
// if (ps->size == 0)
// {
// printf("无数据!\n");
// return;
// }
//
// for (int i = 0; i < ps->size; i++)
// {
// if (ps->arr[i] == x)
// {
// printf("找到了\n");
// return;
// }
// }
//
// printf("找不到\n");
//}
void SLInsert(SL* ps, int pos, SQDataType x)
{
assert(ps && pos < ps->size && ps->size>0);
CheckCapacity( ps, x);
int i = ps->size;
for ( i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[i] = x;
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(pos < ps->size && ps->size>0);
for (int i = pos; i < ps->size; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
1.定义通讯录的结构
这里一定不能忽略前置声明,前置声明的意思是告诉这个头文件我们存在这个顺序表,并且可以使用。
要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作。
有了前置声明,下面的 Contact * con 就是顺序表中的 SL*sl。
#pragma once
#include <stdio.h>
#include <string.h>
#define MAX_NAME 10
#define MAX_GENDER 10
#define MAX_TEL 20
#define MAX_ADDR 20
//定义联系人数据结构:姓名+性别+年龄+电话+地址
typedef struct PeosonInfo
{
char name[MAX_NAME];
char gender[MAX_GENDER];
int age;
char tel[MAX_TEL];
char addr[MAX_ADDR];
}peoInfo;
//前置声明
typedef struct SeqList Contact;
//给顺序表改个名字,叫做通讯录
//注意:这里不能写做typedef SL Contact,而是要用原名。
//因为SL是在struct SeqList定义好了之后才重命名的
//要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作
//有了前置声明,下面的 Contact* con 就是顺序表中的 SL*sl
2.初始化通讯录
void InitContact(Contact* con)//就是相当于SL* sl
{
//实际上就是顺序表的初始化
SLInit(con);
}
3.销毁通讯录
void DestroyContact(Contact* con)
{
SLDestory(con);
}
4.展示联系人数据
展示联系人不能改变其数据内容,所以可以加const进行保护。
void ShowContact(const Contact* con)
{
//表头:姓名+性别+年龄+电话+地址
printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < con->size; i++)
{
printf("%-5s %-5s %-5d %-5s %-5s\n",
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr);
}
printf("\n");
}
5.增加联系人数据
增加通讯录中联系人的数据,实际上就是对顺序表的插入操作,可以使用头插和尾插。这里是尾插。
void AddContact(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);//与顺序表中尾插函数的参数对应
}
6.判断指定联系人是否存在
这个函数是在删除联系人,查找联系人和修改联系人中使用的,因为这三个操作都要先判断该联系人是否存在。若存在,则返回该联系人的位置下标,若不存在则返回-1。
int FindByname(const Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
//找到了
return i;
}
}
//没有找到
return -1;
}
7.删除联系人数据
删除之前要判断这个人是否存在通讯录中,如果这个人存在,说明我们知道他的位置,直接复用顺序表中的对应函数进行删除。
void DelContact(Contact* con)
{
//1.判断要删除的人是否存在
char name[MAX_NAME];
printf("请输入要删除人的姓名:\n");
scanf("%s",name);
int find = FindByname(con, name);
if (find < 0)
{
printf("要删除的联系人不存在!\n");
return;
}
//2.删除:知道了要删除的联系人数据对应的下标
SLErase(con, find);
printf("删除成功!\n");
printf("\n");
}
8.查找联系人数据
在查找之前也要先判断该联系人是否存在,若存在,则打印出该联系人的全部信息;若不存在,则进行提示。
void FindContact(const Contact* con)
{
char name[MAX_NAME];
printf("请输入要查找的人的姓名:\n");
scanf("%s", name);
int find = FindByname(con, name);
if (find < 0)
{
printf("要查找的人不存在!\n");
return;
}
printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-5s %-5s %-5d %-5s %-5s\n",
con->arr[find].name,
con->arr[find].gender,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr);
printf("\n");
}
9.修改联系人数据
修改联系人之前也要判断此人是否存在,存在才修改,不存在则进行提示。
void ModifyContact(Contact* con)
{
//1.判断要修改的联系人是否存在
char name[MAX_NAME];
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");
printf("\n");
}
4. 通讯录完整代码
Contact.h
#pragma once
#include <stdio.h>
#include <string.h>
#define MAX_NAME 10
#define MAX_GENDER 10
#define MAX_TEL 20
#define MAX_ADDR 20
//定义联系人数据结构:姓名+性别+年龄+电话+地址
typedef struct PeosonInfo
{
char name[MAX_NAME];
char gender[MAX_GENDER];
int age;
char tel[MAX_TEL];
char addr[MAX_ADDR];
}peoInfo;
//前置声明
typedef struct SeqList Contact;
//给通讯录改个名字,叫做通讯录
//注意:这里不能写做typedef SL Contact,而是要用原名。
//因为SL是在struct SeqList定义好了之后才重命名的
//要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作
//有了前置声明,下面的 Contact* con 就是顺序表中的 SL*sl
//初始化通讯录
void InitContact(Contact* con);
//添加通讯录数据
void AddContact(Contact* con);
//删除通讯录数据
void DelContact(Contact* con);
//展示通讯录数据
void ShowContact(const Contact* con);
//查找通讯录数据
void FindContact(const Contact* con);
//修改通讯录数据
void ModifyContact(Contact* con);
//销毁通讯录数据
void DestroyContact(Contact* con);
Contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Contact.h"
#include "SeqList.h"
//初始化通讯录
void InitContact(Contact* con)//就是相当于SL* sl
{
//实际上就是顺序表的初始化
SLInit(con);
}
//销毁通讯录数据
void DestroyContact(Contact* con)
{
SLDestory(con);
}
//添加通讯录数据
void AddContact(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);//与顺序表中尾插函数的参数对应
}
//展示通讯录数据
void ShowContact(const Contact* con)
{
//表头:姓名+性别+年龄+电话+地址
printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址");
for (int i = 0; i < con->size; i++)
{
printf("%-5s %-5s %-5d %-5s %-5s\n",
con->arr[i].name,
con->arr[i].gender,
con->arr[i].age,
con->arr[i].tel,
con->arr[i].addr);
}
printf("\n");
}
int FindByname(const Contact* con, char name[])
{
for (int i = 0; i < con->size; i++)
{
if (strcmp(con->arr[i].name, name) == 0)
{
//找到了
return i;
}
}
//没有找到
return -1;
}
//删除指定位置通讯录数据
void DelContact(Contact* con)
{
//1.判断要删除的人是否存在
char name[MAX_NAME];
printf("请输入要删除人的姓名:\n");
scanf("%s",name);
int find = FindByname(con, name);
if (find < 0)
{
printf("要删除的联系人不存在!\n");
return;
}
//2.删除:知道了要删除的联系人数据对应的下标
SLErase(con, find);
printf("删除成功!\n");
printf("\n");
}
//修改通讯录数据
void ModifyContact(Contact* con)
{
//1.判断要修改的联系人是否存在
char name[MAX_NAME];
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");
printf("\n");
}
//查找通讯录数据
void FindContact(const Contact* con)
{
char name[MAX_NAME];
printf("请输入要查找的人的姓名:\n");
scanf("%s", name);
int find = FindByname(con, name);
if (find < 0)
{
printf("要查找的人不存在!\n");
return;
}
printf("%-5s %-5s %-5s %-5s %-5s\n", "姓名", "性别", "年龄", "电话", "地址");
printf("%-5s %-5s %-5d %-5s %-5s\n",
con->arr[find].name,
con->arr[find].gender,
con->arr[find].age,
con->arr[find].tel,
con->arr[find].addr);
printf("\n");
}
Test.c
void menu()
{
printf("************** 通讯录 *************\n");
printf("******* 1.添加联系人 2.删除联系人 *******\n");
printf("******* 3.查找联系人 4.修改联系人 *******\n");
printf("******* 5.展示联系人 0.退出通讯录 *****\n");
printf("******************************************\n");
}
enum option
{
Exit,
addcontact,
delcontact,
findcontact,
modifycontact,
showcontact
};
int main()
{
//TestSeqList();
//ContactTest();
Contact con;
InitContact(&con);
int input = 1;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case Exit:
printf("退出通讯录!\n");
break;
case addcontact:
AddContact(&con);
break;
case delcontact:
DelContact(&con);
break;
case findcontact:
FindContact(&con);
break;
case modifycontact:
ModifyContact(&con);
break;
case showcontact:
ShowContact(&con);
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}