C语言实现通讯录管理系统(结构体+枚举+动态内存开辟+文件操作+线性表存放数据)

Posted 呆呆兽学编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言实现通讯录管理系统(结构体+枚举+动态内存开辟+文件操作+线性表存放数据)相关的知识,希望对你有一定的参考价值。

本篇文章将用C语言代码实现一个通讯录管理系统,本片文章博主将会运用到架构提,枚举,动态内> 存开辟和文件操作等。这里存放数据的结构是线性表。

博主码云gitee链接:https://gitee.com/byte-binxin(需要源码自取)

先给大家展示一张效果图


通讯录菜单栏实现

我们要实现的通讯录应该具备增删查改四种基本功能,还有显示,排序,退出和保存的功能。所以我们会有个功能。实现如下:

void menu()
{
	printf("--------------------------------------\\n");
	printf("------------通讯录管理系统------------\\n");
	printf("------------1.添加联系人--------------\\n");
	printf("------------2.删除联系人--------------\\n");
	printf("------------3.查找联系人--------------\\n");
	printf("------------4.更改联系人信息----------\\n");
	printf("------------5.显示通讯录--------------\\n");
	printf("------------6.对联系人进行排序--------\\n");
	printf("------------7.保存通讯录--------------\\n");
	printf("------------0.退出通讯录--------------\\n");
	printf("--------------------------------------\\n");
}

打印效果如下:

线性表的创建

我们可以先创建一个结构体,里面包含联系人的各种信息。

#define NAME_MAX 20
#define TELE_MAX 12
#define SEX_MAX  5
#define ADDR_MAX 50

typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char tele[TELE_MAX];
	char address[ADDR_MAX];
}PeoInfo;

然后创建一个结构体,其中有用来存放通讯录的大小信息,通讯录的容量和一个结构体指针指向PeoInfo这个结构体。这个指针就可以通过动态开辟内存来调整存放信息的大小。

typedef struct Contact
{
	PeoInfo* data;
	int size;
	int capacity;
}Contact;

初始化通讯录

为了让通讯录能够存放数据,我们需要初始化一下通讯录,首先动态开辟一个3个(根据自己来)PepInfo结构体大小的空间,然后后续根据需求再扩大容量。还要设计一个加载函数,把上一次保存的信息加载进来,这个函数我们后面部分讲解。代码实现如下:

//初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	//开辟三个大小的空间
	pc->data = (PeoInfo*)malloc(sizeof(PeoInfo)* 3);
	if (pc->data==NULL)
	{
		printf("%s", strerror(errno));
		exit(-1);
	}
	
	pc->size = 0;
	pc->capacity = 3;
	//加载数据到通讯录上
	LoadContact(pc);
}

main函数内部结构搭建

我们要让用户选择对应的操作,我们应该提供对应的实现,考虑到多种选择,我们可以用switch语句,还有为了让用户多次选择,我们可以把这个选择放到一个do-while循环里面,根据用户的选择来进行相应的操作。
为了方便之后自己更好的了解哪个对应哪个选择对功能,我们可以创建一个枚举类型,这样我们就可以一目了然哪里该实现哪个功能。

enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	SAVE
};
int main()
{
	//创建一个通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	
	int input = 0;
	do
	{
		//清屏
		system("cls");
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			//增加联系人函数
			break;
		case DEL:
			//删除联系人函数
			break;
		case SEARCH:
			//查找联系人函数
			break;
		case MODIFY:
			//修改联系人函数
			break;
		case SHOW:
			//显示联系人函数
			break;
		case SORT:
			//对联系人进行排序函数
			break;
		case SAVE:
			//保存联系人函数
			break;
		case EXIT:
			//销毁通讯录函数  防止内存泄漏
			printf("退出通讯录\\n");
			break;
		default:
			printf("选择错误\\n");
			system("pause");
			break;
		}
	} while (input);
	return 0;
}

很显然,当用户选择0是就可以退出这个循环了,也就退出了这个程序。

实现功能函数

//初始化通讯录
void InitContact(Contact* pc);
//增加联系人
void AddContact(Contact* pc);
//删除联系人
void DelContact(Contact* pc);
//查找联系人
void SearchContact(Contact* pc);
//更改联系人信息
void ModifyContact(Contact* pc);
//显示联系人
void ShowContact(Contact* pc);
//对通讯录进行排序
void SortContact(Contact* pc);
//保存通讯录
void SaveContact(Contact* pc);
//销毁通讯录
void DestoryContact(Contact* pc);

添加联系人

添加联系人要考虑通讯录容量的问题,所以每次添加联系人的时候就要检查一下通讯录的先有容量,看是否需要扩容,如果通讯录现有容量和size相同,说明通讯录满了,就要扩容了。扩容需要用到realloc函数来扩大data指针的指向空间的大小。

void CheckCapacity(Contact* pc)
{
	if (pc->capacity == pc->size)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(pc->data, sizeof(PeoInfo)*(pc->capacity * 2));
		if (tmp == NULL)
		{
			printf("%s", strerror(errno));
			exit(-1);
		}
		pc->data = tmp;
		pc->capacity *= 2;
		printf("扩容成功\\n");
	}
}

这样就实现了检查通讯录是否需要扩容的函数。

void AddContact(Contact* pc)
{
	assert(pc);
	//检查容量
	CheckCapacity(pc);
	printf("请输入你要添加人的姓名:>");
	scanf("%s", pc->data[pc->size].name);
	printf("请输入你要添加人的性别:>");
	scanf("%s", pc->data[pc->size].sex);
	printf("请输入你要添加人的年龄:>");
	scanf("%d", &(pc->data[pc->size].age));
	printf("请输入你要添加人的电话号码:>");
	scanf("%s", pc->data[pc->size].tele);
	printf("请输入你要添加人的家庭住址:>");
	scanf("%s", pc->data[pc->size].address);
	
	pc->size++;
	printf("增加成功\\n");
	system("pause");
}

显示通讯录

为了让我们更好的观察添加联系人这个功能和其他的功能是否很好的实现了,我们先来实现一个显示通讯录函数来观察一下。
先判断参数,如果通讯录为空就打印一下通讯录为空,然后退出,否则就显示联系人,先打印通讯录信息。
打印之前还要寻找用户要查找的联系人是存在,我们先实现查找联系人的函数。

int FindPeopleByName(const Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->size; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

我们为了好看,可以搭建一个表头,通过循环来控制,可以自己根据训练次数来调节

可以通过格式化输出的"-""%x"来对齐控制,具体看一下如下代码。

//显示联系人
void ShowContact(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录为空\\n");
		system("pause");
		return;
	}
	//打印横线
	int j = 0;
	for (j = 0; j < 21; j++)
	{
		printf("——");
	}
	printf("\\n");
	printf("|%42s\\t\\t\\t\\t\\t\\t\\b\\b\\b\\b\\b|\\n", "通讯录");
	for (j = 0; j < 21; j++)
	{
		printf("——");
	}
	printf("\\n");
	//打印标题
	printf("|%-15s|%-5s|%-5s|%-12s|%-42s\\b|\\n", "姓名", "性别", "年龄", "电话", "家庭住址");
	//打印信息
	int i = 0;
	for (i = 0; i < pc->size; i++)
	{
		for (j = 0; j < 21; j++)
		{
			printf("——");
		}
		printf("\\n");
		printf("|%-15s|%-5s|%-5d|%-12s|%-42s\\b|\\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].address);
	}
	for (j = 0; j < 21; j++)
	{
		printf("——");
	}
	printf("\\n");
	system("pause");
}

下面我们先来看一下添加联系人功能的效果:

删除联系人

删除联系人的第一步首先要检查参数,看通讯录是否为空,空就返回,无法删除,不为空就要查找联系人,看这个联系人是否存在,在哪个位置,这些都是我们需要考虑的,查找联系人的函数在上面我们已经实现过来,这里就不过多介绍。
接下来就是删除了,有两种方式:
第一种相对来说比较简单,就是就通讯录中最后一个人挪动到要删除的那个人的位置,但这样就有一个缺点就是通讯录的顺序发生了改变。
另一种方法相对来说比较麻烦,但其实也很简单,就是把钥删除的位置后面的联系人都往前挪动一个就可以了,具体实现如下:

//删除联系人
void DelContact(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录为空\\n");
		system("pause");
		return;
	}
	else
	{
		char name[NAME_MAX];
		printf("请输入你要删除联系人的姓名:>");
		scanf("%s", name);
		//查找联系人
		int pos = FindPeopleByName(pc,name);
		if (pos == -1)
		{
			printf("该联系人不存在\\n");
			printf("删除失败\\n");
			system("pause");
		}
		else
		{
			//删除
			int i = 0;
			for (i = pos; i < pc->size - 1; i++)
			{
				pc->data[i] = pc->data[i + 1];
			}

			pc->size--;
			printf("删除成功\\n");
			system("pause");
		}
	}
}

动态效果图如下:

查找联系人

查找联系人就是由要用到上面的查找函数,找到后把这个信息打印一遍就可以了。

//查找联系人
void SearchContact(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录为空\\n");
		system("pause");
		return;
	}
	else
	{
		char name[NAME_MAX];
		printf("请输入你要查找联系人的姓名:>");
		scanf("%s", name);
		//查找联系人
		int pos = FindPeopleByName(pc, name);
		if (pos == -1)
		{
			printf("该联系人不存在\\n");
			printf("查找失败\\n");
			system("pause");
		}
		else
		{
			printf("查找成功\\n");
			//打印
			int j = 0;
			for (j = 0; j < 21; j++)
			{
				printf("——");
			}
			printf("\\n");
			//打印标题
			printf("|%-15s|%-5s|%-5s|%-12s|%-42s\\b|\\n", "姓名", "性别", "年龄", "电话", "家庭住址");
			for (j = 0; j < 21; j++)
			{
				printf("——");
			}
			printf("\\n");
			printf("|%-15s|%-5s|%-5d|%-12s|%-42s\\b|\\n",
				pc->data[pos].name,
				pc->data[pos].sex,
				pc->data[pos].age,
				pc->data[pos].tele,
				pc->data[pos].address);
			for (j = 0; j < 21; j++)
			{
				printf("——");
			}
			printf("\\n");
			system("pause");
		}
	}
}

动态效果演示如下:

更改联系人信息

我们先创建一个简易的菜单栏来提示用户要对哪个人的哪个信息进行修改。

void ModifyMenu()
{
	printf("---------------------------\\n");
	printf("-----1.姓名     2.性别-----\\n");
	printf("-----3.年龄     4.电话-----\\n");
	printf("-----5.家庭住址 0.退出-----\\n");
	printf("---------------------------\\n");
}

然后我们可以在ModifyContact函数里面调用,与主函数内部的框进搭建有相似之处,我们来一起看看代码是如何实现的:

//更改联系人信息
void ModifyContact(Contact* pc)
{
	assert(pc);
	if (pc->size == 0)
	{
		printf("通讯录为空\\n");
		system("pause");
		return;
	}
	else
	{
		char name[NAME_MAX];
		printf("请输入你要更改的联系人的姓名:>");
		scanf("%s", name);
		//查找联系人
		int pos = FindPeopleByName(pc, name);
		if (pos == -1)
		{
			printf("该联系人不存在\\n");
			printf("更改信息失败\\n");
			system("pause");
		}
		else
		{
			int input = 0;
			
			do
			{
				system("cls");
				ModifyMenu();
				printf("请选择:>");
				scanf("%d", &input);
				switch (input)
				{
				case 1:
					printf("请输入修改后的姓名:>");
					scanf("%s", pc->data[pos].name);
					以上是关于C语言实现通讯录管理系统(结构体+枚举+动态内存开辟+文件操作+线性表存放数据)的主要内容,如果未能解决你的问题,请参考以下文章

c语言实现静态与动态通讯录

c语言 数据结构线性表

c语言 数据结构线性表

C语言结构体及其内存对齐枚举联合的介绍

C语言☀️自定义类型(结构体+位段+枚举+联合体)建议收藏

C语言自定义数据类型:结构体,枚举,联合