通讯录的简单实现(顺序表实现)

时间:2022-11-17 00:58:59

这是一篇介绍通讯录实现的博客,采用c语言。包含有实现的简单思路、思考方向、代码以及笔者的一些小经验,希望可以帮助到大家,如有错误,还望大佬不吝赐教。 文章稍长,若仅需要某一部分功能,目录可轻易到达。

一、实现思路

1、联系人采用结构体

  • 定义结构体,组成一个人的各类信息;
  • 定义sz,确定已收录的人员数目;
  • 人员之间采用顺序表,便于查找;

2、主要功能

  • 增加 用于在通讯录中,增加联系人; 因采用顺序表实现,只需访问到结构体中合适的位置,故只需一个下标即可,而sz的数目正好指向的顺序表添加元素的下一个位置;

  • 删除 删除条目,需后续位置的信息往前挪,随后修改sz便可;

  • 查询 逐一与结构体中的元素比对,利用strcmp便可实现;

  • 修改 若存在元素,则利用自定义的查询函数找到下标,进行修改;

  • 排序 运用qsort排序函数与自定义的比较函数,可快速实现;

  • 打印 for循环打印便可轻易满足;

二、函数实现及思路

1、结构体的定义

  • 采用define确定结构体的成员大小,便于后续调整;
  • 双层结构体,sz定义在data外部,节省内存空间;
//类型声明,结构体各成员的大小
#define NAME_MAX 20
#define SEX_MAX 5
#define TEL_MAX 15
#define ADDR_MAX 30

//联系表大小
#define MAX 1000

//联系人结构体
typedef struct Peo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peo;

//联系表结构体
typedef struct Contact
{
	Peo data[MAX];
	int sz;
}Contact;

2、主要功能的实现

a、增加
  • 由于顺序表空间有限,需进行判断;
  • 表中存在 sz 个元素时,data 中 sz 下标的位置正好是 sz+1 个元素的位置,因此以sz作为下标,便可轻易实现添加;
//增加联系人
void Add_Contact(Contact* con)
{
	assert(con);
	if (con->sz == MAX)
	{
		printf("通讯录已满!不可添加");
		return;
	}
	printf("请输入名字:");
	scanf("%s", con->data[con->sz].name);
	printf("请输入性别:");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &(con->data[con->sz].age));
	printf("请输入电话:");
	scanf("%s", con->data[con->sz].tel);
	printf("请输入地址:");
	scanf("%s", con->data[con->sz].addr);
	con->sz++;
	printf("添加成功!\n");
}

b、删除
  • 由于删除需要确定删除的具体位置,先定义一个函数,以名字确定位置,若存在则返回下标,不存在返回-1;
  • 删除时也需考虑联系表为空的情况,故需要判断;
  • 存在要删除的人不存在的情况,因此以if语句进行判断;
//以名字搜索
int Search_By_Name(Contact* con, char* cmp)
{
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (strcmp(cmp, con->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}
//按名字删除
void Del_By_Name(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空!不可删除\n");
		return;
	}
	char cmp[20] = { 0 };
	printf("请输入要删除的人:");
	scanf("%s", cmp);
	int x = Search_By_Name(con, cmp);
	if (x != -1)
	{
		for (; x < con->sz; x++)
		{
			con->data[x] = con->data[x + 1];
		}
		con->sz--;
		printf("删除成功!\n");
	}
	else
	{
		printf("要删除的人不存在!\n");
	}
}

c、查询
  • 自定义的以名字查询的函数在实现删除功能时已实现,因此此处便较为方便;
  • 查询成功后,需要打印对应联系人的信息,考虑到后续打印一个人的信息的功能会常用,因此,单独封装成函数;
  • 联系表为空时不可查询,故增加判断;
  • 查抄的人可能不存在,同样进行判断;
//打印指定下标联系人
void PrintP(const Contact* con, int i)
{
	printf("%20s %5s %5d %15s %30s\n", con->data[i].name, con->data[i].sex, con->data[i].age, con->data[i].tel, con->data[i].addr);
}

//查找
void Search(const Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空,不可查找\n");
	}
	int i = 0;
	char cmp[20] = { 0 };
	printf("请输入要查找的人:");
	scanf("%s", cmp);
	i = Search_By_Name(con, cmp);
	if (i != -1)
	{
		printf("信息如下:\n");
		printf("%20s %5s %5s %15s %30s\n", "姓名", "性别", "年龄", "电话", "住址");
		PrintP(con, i);
	}
	else
	{
		printf("查找的人不存在!\n");
	}
}

d、修改
  • 修改前,首先需要查询此联系人;
  • 修改需要确定修改的内容,需确定选项;
  • 以switch语句修改对应的内容;
//修改选项
void Mmenu()
{
	printf("************************************\n");
	printf("*******  1、Name   2、Sex    *******\n");
	printf("*******  3、Age    4、Tel    *******\n");
	printf("*******  5、Addr   0、Exit    ******\n");
	printf("************************************\n");
}
//修改选项
enum Option2
{
	Exit,
	Name,
	Sex,
	Age,
	Tel,
	Addr
};
//修改函数
void Modify(Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空!\n");
	}
	char cmp[20] = { 0 };
	printf("请输入要修改的人的名字:");
	scanf("%s", cmp);
	int i = Search_By_Name(con, cmp);
	if (i == -1)
	{
		printf("此人不存在!\n");
	}
	else
	{
		int input = 0;
		do
		{
			Mmenu();
			printf("%20s %5s %5s %15s %30s\n", "姓名", "性别", "年龄", "电话", "住址");
			PrintP(con, i);
			printf("请输入修改选项:");
			scanf("%d", &input);
			switch (input)
			{
			case Name:
				printf("请输入新名字:");
				scanf("%s", con->data[i].name);
				break;
			case Sex:
				printf("请输入性别:");
				scanf("%s", con->data[i].sex);
				break;
			case Age:
				printf("请输入年龄:");
				scanf("%d", con->data[i].age);
				break;
			case Tel:
				printf("请输入新电话:");
				scanf("%s", con->data[i].tel);
				break;
			case Addr:
				printf("请输入新住址:");
				scanf("%s", con->data[i].addr);
				break;
			case Exit:
				printf("退出修改\n");
				break;
			default:
				break;
			}

		} while (input);
	}
}

e、排序
  • 借用qsort函数,排序的实现较为简单;
  • 自定义函数的实现
//按名字比大小
int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((Peo*)e1)->name, ((Peo*)e2)->name);
}
//按年龄比大小
int cmp_by_age(const void* e1, const void* e2)
{
	return ((Peo*)e1)->age - ((Peo*)e2)->age;
}
//排序选项
void Smenu()
{
	printf("************************************\n");
	printf("*******  1、Name   2、Age    *******\n");
	printf("************************************\n");
}
//通讯录排序
void Sort(Contact* con)
{
	assert(con);
	int input = 0;
	Smenu();
	printf("请选择要排序的方式:");
	scanf("%d", &input);
	int (*pc[3])(const void* e1, const void* e2) = { 0 , cmp_by_name, cmp_by_age };
	qsort(con->data, con->sz, sizeof(Peo), pc[input]);
}
f、打印
  • 借用在查询时定义的打印单个联系人的函数,定义一层for循环,便可满足要求;
//打印通讯录
void PrintC(const Contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		printf("通讯录为空!\n");
	}
	int i = 0;
	printf("%20s %5s %5s %15s %30s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < con->sz; i++)
	{
		PrintP(con, i);
	}
	printf("*****   打印完成   *****\n");
}

三、小tips与头文件

1、枚举的使用

枚举函数的第一个值默认为0,因此,借用枚举函数可以在提高代码可读性的同时,不影响性能;

2、优化方向

  • 对用户的输入没有限制,也未对用户输入进行判断;
  • 联系表的大小不可轻易改变,借用链表,可以实现联系表的动态增加;

3、头文件

//头文件
#pragma once

#include<stdio.h>
#include<string.h>
#include<assert.h>

//枚举提高代码可读性
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	PRINT
};
//类型声明,结构体各成员的大小
#define NAME_MAX 20
#define SEX_MAX 5
#define TEL_MAX 15
#define ADDR_MAX 30
//联系表大小
#define MAX 1000
//联系人结构体
typedef struct Peo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peo;
//联系表结构体
typedef struct Contact
{
	Peo data[MAX];
	int sz;
}Contact;
//函数声明
//菜单
void menu();
//初始化
void Init_Contact(Contact* con);
//增加联系人
void Add_Contact(Contact* con);
//删除联系人
void Del_By_Name(Contact* con);
//搜索联系人
void Search(Contact* con);
//打印通讯录
void PrintC(Contact* con);
//修改联系人
void Modify(Contact* con);
//排序
void Sort(Contact* con);


//测试函数
int main()
{
	int input = 0;
	Contact con;
	Init_Contact(&con);
	do
	{
		menu();
		printf("请输入选项:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			Add_Contact(&con);
			break;
		case DEL:
			Del_By_Name(&con);
			break;
		case SEARCH:
			Search(&con);
			break;
		case MODIFY:
			Modify(&con);
			break;
		case SORT:
			Sort(&con);
			break;
		case PRINT:
			PrintC(&con);
			break;
		case EXIT:
			printf("退出通讯录");
			break;
		default:
			break;
		}
	} while (input);
}