C语言多功能动态通讯录实现示例

前言

本文将模块化地介绍如何实现一个动态开辟空间的通讯录,其有以下九个功能:

  • 打印主菜单
  • 添加联系人
  • 删除联系人
  • 打印通讯录
  • 查找联系人
  • 修改联系人
  • 置顶联系人
  • 排序联系人
  • 退出通讯录

一、预设置

1,头文件的包含

需要用到的头文件有以下几个

#include<stdio.h>//输入输出
#include<stdlib.h>//使用qsort
#include<string.h>//使用strcmp
#include<Windows.h>//使用systeam(“cls”)
#include<assert.h>//使用assert()

2,联系人的信息格式

在此将每个联系人的信息暂定为五项,分别是姓名、性别、年龄、电话、住址,因为要为后面第八个功能——联系人置顶做准备,因此多加入一个int型变量,用于判断该联系人是否被置顶,像这样:

//创建每个联系人的信息格式
typedef struct
{
	char name[max_name];
	char sex[max_sex];
	int age;
	char tele[max_tele];
	char add[max_add];
	int first;//联系人是否被置顶
}people;//联系人类型

以上代码中以max_开头的数据将在稍后的宏定义中介绍。

3,通讯录的信息格式

创建关于通讯录的信息格式的结构体是为了将当前通讯录人数通讯录当前可以容纳的总人数两个变量放在一个结构体变量里面,便于以后的代码传参,像这样:

//创建通讯录格式
typedef struct
{
	people* peoples;//开辟空间后用来维护空间的指针
	int	sz;//当前人数
	int all;//总容纳人数
}contact;通讯录类型

4,通讯录相关宏定义

为了便于日后修改,使用宏定义一些常量。

#define _CRT_SECURE_NO_WARNINGS 1//博主的环境是VS2019
#define init_num_people 10//通讯录初始的人数
#define oncetime 5//通讯录每次扩容增加的人数
#define max_name 10
#define max_sex 4
#define max_tele 11
#define max_add 40

5,通讯录功能枚举

用可以看出含义的英文单词缩写来代替数字,可以方便我们写代码,像这样:

enum MyEnum//对九种通讯录功能进行枚举
{
	MENU = 1,//菜单
	ADD,//添加
	DEL,//删除
	PRIN,//打印
	FIND,//查找
	CHANGE,//修改
	FIRST,//置顶
	SORT,//排序
	EXIT//退出
};

6,基本主函数

总的思路是创建一个刚刚设置的枚举变量,让用户输入数字代表其选择的功能,利用分支语句进入不同的函数。最后不要忘了释放内存哦。

int main()
{
	//创建通讯录
	contact con;
	//初始化
	init_con(&con);
	enum MyEnum input = 0;
	do
	{
	menu();//打印菜单
	scanf("%d", &input);
	switch (input)
	{
	case MENU:
	system("cls");//清屏后break,自动打印菜单
	break;
	case ADD:
	add_people(&con);//增加联系人
	break;
	case DEL:
	del_people(&con);//删除联系人
	break;
	case PRIN:
	PRIN_CON(&con);//打印通讯录
	break;
	case FIND:
	find_people(&con);//查找联系人
	break;
	case CHANGE:
	change_people(&con);//修改联系人
	break;
	case FIRST:
	fir_people(&con);//设置置顶
	break;
	case SORT:
	sort_people(&con);//联系人排序
	break;
	case EXIT:
	free(con.peoples);
	con.peoples = NULL;
	system("cls");
	printf("退出通讯录!\n");
	break;
	}
	} while (input-EXIT);//只有选择9才退出
	return 0;
}

二、功能函数实现

1,初始化函数

此函数中将为通讯录开辟空间,对值进行初始化。

//初始化
void init_con(contact* con)
{
	assert(con);
	con->peoples = (people*)malloc(init_num_people * sizeof(people));//为联系人信息开辟空间
	if (con->peoples == NULL)
	{
	perror("warning::function::9");//开辟失败报错,显示错误位置
	return 0;
	}
	memset(con->peoples, 0, init_num_people * sizeof(people));//把空间初始化为0
	con->sz = 0;//sz是通讯录当前人数
	con->all = init_num_people;//all是通讯录当前可以容纳的总人数
}

2,查找函数

因为通讯录有许多功能(删除,查找,修改,置顶)都需要用到查找,于是写出一个公用函数,用于通过姓名查找通讯录中的联系人,若找到则返回其下标,若找不到则返回sz。

//查找函数,用于通过姓名查找通讯录中的联系人下标,若找到返回下标,若找不到返回sz
int find(contact* con,const char* name)
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
	if (strcmp(name, con->peoples[i].name) == 0)
	{
	return i;
	}
	}
	return i;
}

3,打印菜单

打印菜单,让用户进行选择。

//打印菜单
void menu()
{
	printf("1,打印主菜单\n");
	printf("2,添加联系人\n");
	printf("3,删除联系人\n");
	printf("4,打印通讯录\n");
	printf("5,查找联系人\n");
	printf("6,修改联系人\n");
	printf("7,置顶联系人\n");
	printf("8,联系人排序\n");
	printf("9,退出通讯录\n");
	printf("请选择:");
}

4,增加联系人

为通讯录添加一个联系人。

//增加联系人
void add_people(contact* con)
{
	assert(con);
	system("cls");//为了美观,清屏
	if (con->sz == con->all)
	{
	people* ptr = (people*)realloc(con->peoples, (con->all+oncetime)*sizeof(people));//增加一次扩容的空间
	if (ptr == NULL)
	{
	perror("worning::function::40");
	return 0;
	}
	con->peoples = ptr;
	ptr = NULL;
	memset(con->peoples + con->sz, 0, oncetime * sizeof(people));//把空间初始化为0
	con->all++;
	printf("\n扩容成功\n");
	}
	printf("请输入姓名:");
	scanf("%s", con->peoples[con->sz].name);
	printf("请输入性别:");
	scanf("%s", con->peoples[con->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &con->peoples[con->sz].age);
	printf("请输入电话:");
	scanf("%s", con->peoples[con->sz].tele);
	printf("请输入住址:");
	scanf("%s", con->peoples[con->sz].add);
	con->sz++;
	system("cls");
	printf("添加成功!\n");
}

5,删除联系人

利用查找函数,找到后删除,不要忘记将后面的联系人信息向前移。

//删除联系人
void del_people(contact* con)
{
	assert(con);
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要删除的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人\n", h);
	return;
	}
	memset(con->peoples + flag, 0, sizeof(people));
	int i = 0;
	for (i = flag; i < con->sz-1; i++)
	{
	people tmp = con->peoples[i];
	con->peoples[i] = con->peoples[i + 1];
	con->peoples[i + 1] = tmp;
	}
	con->sz--;
	system("cls");
	printf("删除成功!\n");
}

6,查找联系人

通讯录人太多了怎么办?没关系,你可以使用查找,该函数后面的打印格式将与打印函数模块相同

//查找联系人
void find_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要查找的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	system("cls");
	printf("您的通讯录中没有名为“%s”的联系人\n", h);
	return;
	}
	printf("********************************************\n");
	printf("姓名 性别 年龄 电话 住址\n");
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[flag].name, con->peoples[flag].sex, con->peoples[flag].age,
	con->peoples[flag].tele, con->peoples[flag].add);
	printf("********************************************\n");
}

7,修改联系人

老样子,先查找,找到以后让用户再输入一次。

//修改联系人
void change_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要修改的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人", h);
	return;
	}
	printf("您想将姓名修改为:");
	scanf("%s", con->peoples[flag].name);
	printf("您想将性别修改为:");
	scanf("%s", con->peoples[flag].sex);
	printf("您想将年龄修改为:");
	scanf("%d", &con->peoples[flag].age);
	printf("您想将电话修改为:");
	scanf("%s", con->peoples[flag].tele);
	printf("您想将住址修改为:");
	scanf("%s", con->peoples[flag].add);
	printf("修改完成!\n");
}

8,设置置顶

将联系人设为通讯录的置顶,无论在任何的排序情况下,都会先打印置顶的联系人。当然,你也可以取消置顶。

//设置置顶
void fir_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要置顶或取消置顶的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人", h);
	return;
	}
	if (con->peoples[flag].first == 1)//成员变量first为1则已经被置顶
	{
	con->peoples[flag].first = 0;
	}
	else if (con->peoples[flag].first == 0)//成员变量first为0则还没被置顶
	{
	con->peoples[flag].first = 1;
	}
	printf("设置成功!\n");
}

9,联系人排序

在此设置了三种排序方式,排序方法利用qsort。

1,性别(先女后男)

2,年龄(由小到大)

3,电话(由小到大)

//联系人排序
int sex_cmp(const void* str1,const void* str2)//性别排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return -strcmp(a->sex, b->sex);
}
int age_cmp(const void* str1, const void* str2)//年龄排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return (a->age - b->age);
}
int tele_cmp(const void* str1, const void* str2)//电话排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return strcmp(a->tele, b->tele);
}
void sort_people(contact* con)
{
	system("cls");
	printf("请选择排序标准\n");
	printf("1,性别\n");
	printf("2,年龄\n");
	printf("3,电话\n");
	printf("请选择:");
	int n = 0;
	scanf("%d", &n);
	if(n==1)
	qsort(con->peoples, con->sz, sizeof(people), sex_cmp);
	else if(n==2)
	qsort(con->peoples, con->sz, sizeof(people), age_cmp);
	else if(n==3)
	qsort(con->peoples, con->sz, sizeof(people), tele_cmp);
	printf("排序成功!\n");
	PRIN_CON(con);
}

10,联系人打印

打印所有的联系人,因为有置顶功能的存在,需要遍历两次,第一次打印被置顶的联系人,第二次打印没有被置顶的联系人,像这样:

//打印通讯录
void PRIN_CON(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("********************************************\n");
	printf("姓名 性别 年龄 电话 住址\n");
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
	if(con->peoples[i].first==1)
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[i].name, con->peoples[i].sex, con->peoples[i].age, 
	con->peoples[i].tele, con->peoples[i].add);
	}
	for (i = 0; i < con->sz; i++)
	{
	if (con->peoples[i].first != 1)
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[i].name, con->peoples[i].sex, con->peoples[i].age,
	con->peoples[i].tele, con->peoples[i].add);
	}
	printf("********************************************\n");
}

至于这打印格式,保持一致即可。

三、优化思路

1,查找函数优化

利用strstr实现对信息的检索,就不必死板地搜索全名了,例如你可以搜索“张三”,以此检索在姓名、地址、简介等所有包括“张三”的信息并返回其联系人的信息。

2,额外功能优化

可以额外增加收藏夹,黑名单,群组等等功能,在联系人信息格式里面多加几个变量即可,只是在打印的时候需要多加注意一些相悖的功能,例如进入黑名单后应自动取消置顶、收藏等。且打印时不再打印黑名单内的联系人。

3,打印优化

设置更多的打印方法,例如单独打印黑名单,单独打印置顶,单独打印收藏夹等等。亦可以设置打印打印联系人的某项信息,例如只打印姓名,只打印电话等等。

四、源代码

下面放出源代码,可以直接用哦

1,head.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<Windows.h>
#include<assert.h>
#define init_num_people 10//通讯录初始的人数
#define oncetime 5//通讯录每次扩容增加的人数
#define max_name 10
#define max_sex 4
#define max_tele 11
#define max_add 40
enum MyEnum//对九种通讯录功能进行枚举
{
	MENU = 1,
	ADD,
	DEL,
	PRIN,
	FIND,
	CHANGE,
	FIRST,
	SORT,
	EXIT
};
//创建每个联系人的信息格式
typedef struct
{
	char name[max_name];
	char sex[max_sex];
	int age;
	char tele[max_tele];
	char add[max_add];
	int first;//联系人是否被置顶
}people;
//创建通讯录格式
typedef struct
{
	people* peoples;//用来维护联系人信息的指针
	int	sz;//当前人数
	int all;//总容纳人数
}contact;
//功能函数
void init_con(contact* con);//初始化
void menu();//打印菜单
void PRIN_CON(contact* con);//打印通讯录
void find_people(contact* con);//查找联系人
void change_people(contact* con);//修改联系人
void fir_people(contact* con);//设置置顶
void sort_people(contact* con);//联系人排序
//常用函数
int find(contact* con, const char* name);//查找函数,参数2为需要查找的姓名,若找到返回下标,找不到返回sz

2,sour.c

#include"head.h"
int main()
{
	//创建通讯录
	contact con;
	//初始化
	init_con(&con);
	enum MyEnum input = 0;
	do
	{
	//打印菜单
	menu();
	scanf("%d", &input);
	switch (input)
	{
	case MENU:
	system("cls");//清屏后break,自动打印菜单
	break;
	case ADD:
	add_people(&con);//增加联系人
	break;
	case DEL:
	del_people(&con);//删除联系人
	break;
	case PRIN:
	PRIN_CON(&con);//打印通讯录
	break;
	case FIND:
	find_people(&con);//查找联系人
	break;
	case CHANGE:
	change_people(&con);//修改联系人
	break;
	case FIRST:
	fir_people(&con);//设置置顶
	break;
	case SORT:
	sort_people(&con);//联系人排序
	break;
	case EXIT:
	free(con.peoples);
	con.peoples = NULL;
	system("cls");
	printf("退出通讯录!\n");
	break;
	}
	} while (input-EXIT);//只有选择9才退出
	return 0;
}

3,function.c

#include"head.h"
//初始化
void init_con(contact* con)
{
	assert(con);
	con->peoples = (people*)malloc(init_num_people * sizeof(people));//为联系人信息开辟空间
	if (con->peoples == NULL)
	{
	perror("warning::function::9");//开辟失败报错,显示错误位置
	return 0;
	}
	memset(con->peoples, 0, init_num_people * sizeof(people));//把空间初始化为0
	con->sz = 0;
	con->all = init_num_people;
}
//打印菜单
void menu()
{
	printf("1,打印主菜单\n");
	printf("2,添加联系人\n");
	printf("3,删除联系人\n");
	printf("4,打印通讯录\n");
	printf("5,查找联系人\n");
	printf("6,修改联系人\n");
	printf("7,置顶联系人\n");
	printf("8,联系人排序\n");
	printf("9,退出通讯录\n");
	printf("请选择:");
}
//增加联系人
void add_people(contact* con)
{
	assert(con);
	system("cls");//为了美观,清屏
	if (con->sz == con->all)
	{
	people* ptr = (people*)realloc(con->peoples, (con->all+oncetime)*sizeof(people));//增加一次扩容的空间
	if (ptr == NULL)
	{
	perror("worning::function::40");
	return 0;
	}
	con->peoples = ptr;
	ptr = NULL;
	memset(con->peoples + con->sz, 0, oncetime * sizeof(people));//把空间初始化为0
	con->all++;
	printf("\n扩容成功\n");
	}
	printf("请输入姓名:");
	scanf("%s", con->peoples[con->sz].name);
	printf("请输入性别:");
	scanf("%s", con->peoples[con->sz].sex);
	printf("请输入年龄:");
	scanf("%d", &con->peoples[con->sz].age);
	printf("请输入电话:");
	scanf("%s", con->peoples[con->sz].tele);
	printf("请输入住址:");
	scanf("%s", con->peoples[con->sz].add);
	con->sz++;
	system("cls");
	printf("添加成功!\n");
}
//查找函数,用于通过姓名查找通讯录中的联系人下标,若找到返回下标,若找不到返回sz
int find(contact* con,const char* name)
{
	assert(con);
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
	if (strcmp(name, con->peoples[i].name) == 0)
	{
	return i;
	}
	}
	return i;
}
//删除联系人
void del_people(contact* con)
{
	assert(con);
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要删除的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人\n", h);
	return;
	}
	memset(con->peoples + flag, 0, sizeof(people));
	int i = 0;
	for (i = flag; i < con->sz-1; i++)
	{
	people tmp = con->peoples[i];
	con->peoples[i] = con->peoples[i + 1];
	con->peoples[i + 1] = tmp;
	}
	con->sz--;
	system("cls");
	printf("删除成功!\n");
}
//打印通讯录
void PRIN_CON(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("********************************************\n");
	printf("姓名 性别 年龄 电话 住址\n");
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
	if(con->peoples[i].first==1)
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[i].name, con->peoples[i].sex, con->peoples[i].age, 
	con->peoples[i].tele, con->peoples[i].add);
	}
	for (i = 0; i < con->sz; i++)
	{
	if (con->peoples[i].first != 1)
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[i].name, con->peoples[i].sex, con->peoples[i].age,
	con->peoples[i].tele, con->peoples[i].add);
	}
	printf("********************************************\n");
}
//查找联系人
void find_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要查找的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	system("cls");
	printf("您的通讯录中没有名为“%s”的联系人\n", h);
	return;
	}
	printf("********************************************\n");
	printf("姓名 性别 年龄 电话 住址\n");
	printf("%-10s%-8s%-6d%-11s%s\n", con->peoples[flag].name, con->peoples[flag].sex, con->peoples[flag].age,
	con->peoples[flag].tele, con->peoples[flag].add);
	printf("********************************************\n");
}
//修改联系人
void change_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要修改的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人", h);
	return;
	}
	printf("您想将姓名修改为:");
	scanf("%s", con->peoples[flag].name);
	printf("您想将性别修改为:");
	scanf("%s", con->peoples[flag].sex);
	printf("您想将年龄修改为:");
	scanf("%d", &con->peoples[flag].age);
	printf("您想将电话修改为:");
	scanf("%s", con->peoples[flag].tele);
	printf("您想将住址修改为:");
	scanf("%s", con->peoples[flag].add);
	printf("修改完成!\n");
}
//设置置顶
void fir_people(contact* con)
{
	system("cls");
	if (con->sz == 0)
	{
	printf("您的通讯录目前没有联系人\n");
	return;
	}
	printf("请输入您要置顶或取消置顶的联系人姓名:");
	char h[10];
	scanf("%s", h);
	int flag = find(con, h);
	if (flag == con->sz)
	{
	printf("您的通讯录中没有名为“%s”的联系人", h);
	return;
	}
	if (con->peoples[flag].first == 1)
	{
	con->peoples[flag].first = 0;
	}
	else if (con->peoples[flag].first == 0)
	{
	con->peoples[flag].first = 1;
	}
	printf("设置成功!\n");
}
//联系人排序
int sex_cmp(const void* str1,const void* str2)//性别排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return -strcmp(a->sex, b->sex);
}
int age_cmp(const void* str1, const void* str2)//年龄排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return (a->age - b->age);
}
int tele_cmp(const void* str1, const void* str2)//电话排序
{
	people* a = (people*)str1;
	people* b = (people*)str2;
	return strcmp(a->tele, b->tele);
}
void sort_people(contact* con)
{
	system("cls");
	printf("请选择排序标准\n");
	printf("1,性别\n");
	printf("2,年龄\n");
	printf("3,电话\n");
	printf("请选择:");
	int n = 0;
	scanf("%d", &n);
	if(n==1)
	qsort(con->peoples, con->sz, sizeof(people), sex_cmp);
	else if(n==2)
	qsort(con->peoples, con->sz, sizeof(people), age_cmp);
	else if(n==3)
	qsort(con->peoples, con->sz, sizeof(people), tele_cmp);
	printf("排序成功!\n");
	PRIN_CON(con);
}
作者:in绯原文地址:https://cloud.tencent.com/developer/article/2211110?areaSource=101001.5&traceId=ZZvCvJQIPcWwHuFLq0ahr

%s 个评论

要回复文章请先登录注册