admin管理员组

文章数量:1487745

通讯录进阶,支持动态内存与数据存储的通讯录

如果你没有看过通讯录初级,请先移步到那。 在前面我们写了一个静态的通讯录,通讯录的大小都是固定的,同时每次启动通讯录都需要重新输入数据。这用起来肯定是不人性化的。 进阶通讯录与原版的不同点就在于,通讯录的大小是会随着数据的添加而增大的,也就是动态内存管理,以及会对通讯录的数据进行存储,一旦通讯录被关闭数据就会自动存放在相应的文件。同时在下一次打开通讯录时,会自动将文件中的数据导入通讯录中。

1.对结构体contact的修改

代码语言:javascript代码运行次数:0运行复制
//静态
//typedef struct contact
//{
//	buddies data[MAX];
//	int size;
//}contact;
//动态
typedef struct contact
{
	buddies* data;
	int size;
	int capacity;
}contact;

capacity的添加是为了可以清楚的知道目前我们为通讯录开放的多大的空间。

2.对通讯录初始化的修改

代码语言:javascript代码运行次数:0运行复制
//静态
//void InitContact(contact* pc)
//{
//	memset(pc->data, 0, sizeof(pc->data));
//	pc->size = 0;
//}
//动态
void InitContact(contact* pc)
{
	pc->data = (buddies*)malloc(sizeof(buddies) * 2);
	pc->size = 0;
	pc->capacity = 2;
}

动态内存开辟,这里我们的规定是初始给2个联系人的空间,你可以自行修改。然后将malloc开辟空间是地址给pc->data

3.对通讯录添加数据的修改

代码语言:javascript代码运行次数:0运行复制
//静态
//void AddContact(contact* pc)
//{
//	if (pc->size > MAX)
//	{
//		printf("通讯录已满\n");
//		return;
//	}
//	printf("请输入姓名:>");
//	scanf("%s", pc->data[pc->size].name);
//	printf("请输入年龄:>");
//	scanf("%d", &pc->data[pc->size].age);
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->size].sex);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->size].tel);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->size].add);
//	printf("联系人添加完成\n");
//	//添加成功,size要加1
//	pc->size += 1;
//}

int check_capacity(contact* pc)//检查当前容量是否需要进行扩容
{
	if (pc->size == pc->capacity)//扩容了
	{
		buddies* tmp = (buddies*)realloc(pc->data, sizeof(buddies) * (pc->capacity + 2));//利用realloc进行扩容,每次添加2个空间的大小,最佳扩容方法应该是每次扩大1.5倍~2倍。因为频繁的扩容容易造成内存碎片增多
		if (tmp == NULL)
		{
			//扩容失败
			perror("realloc");
			return 0;
		}
		pc->data = tmp;
		pc->capacity += 2;
		printf("扩容成功\n");
		return 1;
	}
	else//还没满
	{
		return 1;
	}
}

//动态
void AddContact(contact* pc)
{
	if (check_capacity(pc) == 0)
	{
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->size].name);
	printf("请输入年龄:>");
	scanf("%d", &pc->data[pc->size].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->size].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->size].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->size].add);
	printf("联系人添加完成\n");
	//添加成功,size要加1
	pc->size += 1;
}

因为原版通讯录的大小是固定的,只需要检查空间是否满了就可以了。而动态的通讯录在检查后还需要进行扩容,扩容失败返回-1,没满返回1,扩容成功也返回1。AddContact函数再根据返回值来做下一步大操作。

3.通讯录的销毁

因为通讯录的空间是动态内存开辟的,所以再最后是需要还给存储系统的,我们需要释放空间。 至于什么时候释放,当用户退出程序时释放空间。

代码语言:javascript代码运行次数:0运行复制
void DestoryContact(contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->size = 0;
	pc->capacity = 0;
}

int main()
{
	int input = 0;
	contact ct;
	InitContact(&ct);
	do
	{
		menu();//菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&ct);
			break;
		case DELE:
			DeleteContact(&ct);
			break;
		case SEAR:
			SearchContact(&ct);
			break;
		case REVI:
			ReviseContact(&ct);
			break;
		case SHOW:
			ShowContact(&ct);
			break;
		case SORT:
			SortContact(&ct);
			break;
		case EXIT:
			DestoryContact(&ct);//空间释放
			printf("退出程序\n");
			break;
		default:
			printf("请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

用free函数来释放空间,然后让指针置为NULL。

4.利用文件保存通讯录数据

这里我们利用fwrite将数据转换为二进制存入文件。 然后这个函数需要放在退出程序的位置,当程序退出前就会自动保存数据。

代码语言:javascript代码运行次数:0运行复制
void SaveContact(contact* pc)
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//写入文件
	fwrite(pc->data, sizeof(buddies), pc->size, pf);
	fclose(pf);
	pf = NULL;
}

5.将文件数据导入通讯录

利用fread将文件中的二进制数据导入通讯录,我们先创立一个临时的变量来存放数据,然后在把这个数据转移给通讯录。 该函数一个放在通讯录初始化完后。

代码语言:javascript代码运行次数:0运行复制
void LoadContact(contact* pc)
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//读取文件
	buddies tmp = { 0 };
	while (fread(&tmp, sizeof(buddies), 1, pf))
	{
		if (check_capacity(pc) == 0)
		{
			return;
		}
		pc->data[pc->size] = tmp;
		pc->size += 1;
	}

	fclose(pf);
	pf = NULL;
}

代码整合

代码语言:javascript代码运行次数:0运行复制
//contact.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>

#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TEL 16
#define MAX_ADD 30

enum select
{
	EXIT,
	ADD,
	DELE,
	SEAR,
	REVI,
	SHOW,
	SORT
};

typedef struct buddies
{
	char name[MAX_NAME];//姓名
	int age;//年龄
	char sex[MAX_SEX];//性别
	char tel[MAX_TEL];//电话
	char add[MAX_ADD];//地址
}buddies;

//静态

//typedef struct contact
//{
//	buddies data[MAX];
//	int size;
//}contact;

typedef struct contact
{
	buddies* data;
	int size;
	int capacity;
}contact;

void InitContact(contact* pc);

void AddContact(contact* pc);

void DeleteContact(contact* pc);

void SearchContact(contact* pc);

void ReviseContact(contact* pc);

void ShowContact(contact* pc);

void SortContact(contact* pc);

void DestoryContact(contact* pc);

void SaveContact(contact* pc);

void LoadContact(contact* pc);

//contact.c
#include "contact.h"

//静态
//void InitContact(contact* pc)
//{
//	memset(pc->data, 0, sizeof(pc->data));
//	pc->size = 0;
//}


void InitContact(contact* pc)
{
	pc->data = (buddies*)malloc(sizeof(buddies) * 2);
	pc->size = 0;
	pc->capacity = 2;
}

//void AddContact(contact* pc)
//{
//	if (pc->size > MAX)
//	{
//		printf("通讯录已满\n");
//		return;
//	}
//	printf("请输入姓名:>");
//	scanf("%s", pc->data[pc->size].name);
//	printf("请输入年龄:>");
//	scanf("%d", &pc->data[pc->size].age);
//	printf("请输入性别:>");
//	scanf("%s", pc->data[pc->size].sex);
//	printf("请输入电话:>");
//	scanf("%s", pc->data[pc->size].tel);
//	printf("请输入地址:>");
//	scanf("%s", pc->data[pc->size].add);
//	printf("联系人添加完成\n");
//	//添加成功,size要加1
//	pc->size += 1;
//}

int check_capacity(contact* pc)
{
	if (pc->size == pc->capacity)//扩容了
	{
		buddies* tmp = (buddies*)realloc(pc->data, sizeof(buddies) * (pc->capacity + 2));
		if (tmp == NULL)
		{
			//扩容失败
			perror("realloc");
			return 0;
		}
		pc->data = tmp;
		pc->capacity += 2;
		printf("扩容成功\n");
		return 1;
	}
	else
	{
		return 1;
	}
}

//动态
void AddContact(contact* pc)
{
	if (check_capacity(pc) == 0)
	{
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pc->size].name);
	printf("请输入年龄:>");
	scanf("%d", &pc->data[pc->size].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->size].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->size].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->size].add);
	printf("联系人添加完成\n");
	//添加成功,size要加1
	pc->size += 1;
}

//找到返回下标,没找到返回-1
int FindCon(contact* pc)
{
	char tmp[MAX_NAME] = { 0 };
	printf("请输入姓名:>");
	scanf("%s", tmp);
	for (int i = 0; i < pc->size; ++i)
	{
		if (strcmp(tmp, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}
void DeleteContact(contact* pc)
{
	//通讯录为空,就退出
	if (pc->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	//查找要删除的联系人
	int pos = FindCon(pc);
	if (pos == -1)
	{
		printf("找不到\n");
		return;
	}
	//因为有一个数据要被删除,当它被删除时,其后面的数据需要挪动到前面一格。
	for (int i = pos; i < pc->size - 1; ++i)
	{
		pc->data[i] = pc->data[i + 1];//后面数据往前覆盖
	}
	printf("删除联系人成功\n");
	pc->size -= 1;
}

void SearchContact(contact* pc)
{
	//通讯录为空,就退出
	if (pc->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	//查找要打印的联系人
	int pos = FindCon(pc);
	if (pos == -1)
	{
		printf("找不到\n");
		return;
	}
	printf("%-10s\t%-5s\t%-6s\t%-16s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
	printf("%-10s\t%-5d\t%-6s\t%-16s\t%-20s\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].tel,
		pc->data[pos].add);

}

void ReviseContact(contact* pc)
{
	//通讯录为空,就退出
	if (pc->size == 0)
	{
		printf("通讯录为空\n");
		return;
	}
	//查找要修改的联系人
	int pos = FindCon(pc);
	if (pos == -1)
	{
		printf("找不到\n");
		return;
	}
	printf("请输入姓名:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &pc->data[pos].age);
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tel);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].add);
	printf("联系人修改完成\n");
}

void ShowContact(contact* pc)
{
	printf("%-10s\t%-5s\t%-6s\t%-16s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i < pc->size; ++i)
	{
		printf("%-10s\t%-5d\t%-6s\t%-16s\t%-20s\n", pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].tel,
			pc->data[i].add);
	}
}

int cmp_contact(void* p1, void* p2)
{
	return strcmp(((buddies*)p1)->name, ((buddies*)p2)->name);
}
void SortContact(contact* pc)
{
	qsort(pc->data, pc->size, sizeof(buddies), cmp_contact);
	ShowContact(pc);
}

void DestoryContact(contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->size = 0;
	pc->capacity = 0;
}

void SaveContact(contact* pc)
{
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//写入文件
	fwrite(pc->data, sizeof(buddies), pc->size, pf);
	fclose(pf);
	pf = NULL;
}



void LoadContact(contact* pc)
{
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}
	//读取文件
	buddies tmp = { 0 };
	while (fread(&tmp, sizeof(buddies), 1, pf))
	{
		if (check_capacity(pc) == 0)
		{
			return;
		}
		pc->data[pc->size] = tmp;
		pc->size += 1;
	}

	fclose(pf);
	pf = NULL;
}

//test.c
#include "contact.h"

void menu()
{
	printf("******************************\n");
	printf("****1.ADD       2.DELE    ****\n");
	printf("****3.SEAR      4.REVI    ****\n");
	printf("****5.SHOW      6.SORT    ****\n");
	printf("****0.EXIT                ****\n");
	printf("******************************\n");
}
int main()
{
	int input = 0;
	contact ct;
	InitContact(&ct);
	LoadContact(&ct);
	do
	{
		menu();//菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&ct);
			break;
		case DELE:
			DeleteContact(&ct);
			break;
		case SEAR:
			SearchContact(&ct);
			break;
		case REVI:
			ReviseContact(&ct);
			break;
		case SHOW:
			ShowContact(&ct);
			break;
		case SORT:
			SortContact(&ct);
			break;
		case EXIT:
			SaveContact(&ct);
			DestoryContact(&ct);
			printf("退出程序\n");
			break;
		default:
			printf("请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-10-15,如有侵权请联系 cloudcommunity@tencent 删除数据存储datasize程序数据

本文标签: 通讯录进阶,支持动态内存与数据存储的通讯录