基于顺序表 --- 实现简易【通讯录】

1. 多文件操作

SeqList.h //用于定义顺序表的结构,增删查改等函数的声明
SeqList.c //用于实现增删查改等函数
Contact.h //用于定义通讯录的结构,通讯录中联系人的增删查改等函数的声明
Contact.c //用于实现通讯录中增删查改等函数
Test.h //用于测试上述实现的函数,和菜单界面

2. 通讯录的主要功能

1.初始化通讯录
2.销毁通讯录
3.展示联系人数据
4.增加联系人数据
5.判断指定联系人是否存在
6.删除联系人数据
7.查找联系人数据
8.修改联系人数据

对通讯录中各个功能的展示:

在这里插入图片描述

3. 通讯录的实现

想要基于顺序表实现通讯录,首先我们需要一个顺序表。由于顺序表的实现比较简单,这里直接给出,不进行详细说明 (如想要详细了解顺序表的实现,请前往我的主页查看)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ddb3977b5f2740a7bbdc3e4a4f8d10dc.png

说明:下面两个文件中注释的部分是在顺序表类型替换后(由整型替换为结构体类型)的不适用的部分。读者不必在意。

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.定义通讯录的结构

由于我们这里需要用到SeqList.h中的struct SeqList结构体,很多人觉得只要在Contact .h头文件中包含SeqList.h就行,其实这是错误的,因为在SeqList.h中已经包含了Contact .h头文件是不能交叉包含的

所以我们这里用了另一个方法,叫做前置声明,前置声明的意思是告诉这个头文件我们存在这个顺序表,并且可以使用。

要用到顺序表相关的方法,对通讯录的操作实际上就是对顺序表的操作。
有了前置声明,下面的 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;
}
  • 42
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 45
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值