C语言小程序-通讯录(动态内存管理)

设计思路讲解:
我们以手机上的通讯录为例,用C来设计一个简单的通讯录版本
通讯录的功能参照手机上的版本应该有增加,删除,修改,查找,因为我们的代码最终是在终端显示的,所以我们再给出一个显示通讯录的功能,以及最后对通讯录进行排序的功能。

我们使用结构体来集合关于通讯录中联系人的信息(定义为PeoInfo),这就需要我们要有一个关于PeoInfo的集合来存放多个人的信息,集合的大小可以由动态内存函数来开辟,内存空间不足时自动增加空间以便增添联系人。

删除时,有很多的方法,可以将要删除的联系人的信息全部改为0,然后qsort一下就ok了。也可以用该联系人后面的联系人覆盖掉要删除的,这样也能实现。方法还有很多,这里不一一给出了。

修改时我们要先输入该联系人的名字,然后重新输入信息。

查找也是要先输入联系人的名字,然后显示联系人的信息,我们发现在实现删除,修改查找等功能时,都要涉及到输入联系人的名字进行插查询,所以我们在写代码时,可以将这一功能封装成一个函数,以减少我们重复的代码。

显示通讯录就很容易实现了,只需要将所有的联系人信息全部打印出来就好了,这里可以使用循环来解决。

排序我们就使用qsort进行,可以按照名字,年龄,或者其他的信息来进行排
序,这个取决于程序员自己。

接下来我们就来开始对各个功能的代码实现,在最后会给出最后的代码。有基础的小伙伴可以直接跳到最后查看最终代码。

初始化通讯录

首先要创建一个通讯录出来才能对其初始化,前面我们说过使用PeoInfo来收集联系人的信息

struct PeoInfo
{
char name[MAX_NAME];
char sex[SEX];
int age;
char telephone[TelePhone];
};

大小我们统一使用宏定义来操作,方便我们后续的修改。
只有一个结构体行吗??
肯定是不行的,因为我们还需要一个集合体来存放PeoInfo的信息,所以还要有一个结构体,这个结构体包含我们的PeoInfo。

//通讯录结构体
struct Contact
{
//创建sz的目的是为了在进行添加删除等操作时,好找到元素的下标
int sz;
struct PeoInfo* arr;
int capacity;//容量大小.
};

这还给出了sz和capacity两个参数,capacity是记录通讯录结构体的大小,sz是用来记录当前联系人的个数,以方便我们后续的增加,删除和查找等功能的实现,这一点如果能想到的话,后面的实现就很容易了,因为后续的大多功能都是以这两个参数为工具进行的。

结构体创建好了之后,我们就要对其初始化了,除了capacity需要给定一个初始大小之外,剩下的都要全部初始化为0。

//初始化为全0;
void InitContact(struct Contact* pc)
{
assert(pc);
pc->sz = 0;
pc->capacity = 3;//默认容量大小是3
pc->arr = (struct PeoInfo*)malloc(3 * sizeof(struct PeoInfo));
if (pc->arr == NULL)
{
strerror(errno);
return; //exit(-1)
}
}

我们把有关通讯录的实现的代码放到contact.c的文件中,把所有包含的头文件放在contact.h的头文件当中,把代码的主主体躯干放在test.c的测试文件当中。

相关功能的实现(增删查改显排)

剩下功能的实现以及注意事项我们在代码中注释了,大家留意一下就好,另外我把上面初始化的代码也一并在这里给大家展示出来,这里的代码就是contact.c中的代码啦~

#include "contact.h"

//初始化为全0;
void InitContact(struct Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	pc->capacity = 3;//默认容量大小是3
	pc->arr = (struct PeoInfo*)malloc(3 * sizeof(struct PeoInfo));
	if (pc->arr == NULL)
	{
		strerror(errno);//打印错误信息
		return;
	}
}

//增加联系人
void ADDContact(struct Contact* pc)
{
	assert(pc);
	//如果空间已满的话,要先增加一定的空间
	if (pc->sz == pc->capacity)
	{
		//增加容量
		struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->arr, (pc->capacity + 2)*sizeof(struct PeoInfo));//一次扩充两个大小的空间
		if (ptr == NULL)
		{
			strerror(errno);//对应的头文件<string.h>, <errno.h>
			return;
		}
		else
		{
			pc->capacity += 2;
			pc->arr = ptr;
			ptr = NULL;
			printf("增容成功!\n");
		}
	}

	//增加
	printf("请输入名字:>");
	scanf("%s", pc->arr[pc->sz].name);
	printf("请输入性别:>");
	scanf("%s", pc->arr[pc->sz].sex);
	printf("请输入年龄:>");
	scanf("%d", &(pc->arr[pc->sz].age));
	printf("请输入电话:>");
	scanf("%s", pc->arr[pc->sz].telephone);

	//sz++指向下一个空白的元素,方便下次直接添加联系人
	pc->sz++;
	printf("成功增加联系人\n");
}

//将查找指定联系人封装成一个函数,因为在后续的功能中还要用到这个
int FindByName(const struct Contact* pc, char* n)
{
	assert(pc && n);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//strcmp比较两个字符串是否相同
		if (0 == strcmp(pc->arr[i].name, n))
		{
			return i;
		}
	}
	return -1;
}

//删除void DeleContact(struct Contact* pc);

void DeleContact(struct Contact* pc)
{
	assert(pc);
	char n[MAX_NAME];
	printf("请输入查找联系人的名字:>");
	scanf("%s", n);
	int ret = FindByName(pc, n);
	if (ret == -1)
		printf("该联系人不存在\n");
	else
	{
		//删除
		for (int j = ret; j < pc->sz - 1; j++)
		{
			pc->arr[j] = pc->arr[j + 1];
		}
		//记得减减!!!!
		pc->sz--;
		printf("成功删除指定联系人\n");
		//方法2,qsort排序,将要删除的人的数据改为0,再qsort排序一下也行,同样记得pc->sz--;
	}


}

//修改
void ModifyContact(struct Contact* pc)
{
	assert(pc);
	printf("请输入要修改人的名字:>");
	char name[MAX_NAME];
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (ret == -1)
		printf("要修改的人不存在\n");
	else
	{
		//将要修改的人的信息重新录入,并提示修改成功!
		printf("请输入名字:>");
		scanf("%s", pc->arr[ret].name);
		printf("请输入性别:>");
		scanf("%s", pc->arr[ret].sex);
		printf("请输入年龄:>");
		scanf("%d", &(pc->arr[ret].age));
		printf("请输入电话:>");
		scanf("%s", pc->arr[ret].telephone);

		printf("修改成功\n");
	}
}

//查找
void SearchContact(const struct Contact* pc)
{
	assert(pc);
	char name[MAX_NAME];
	printf("请输入要查找的人的名字:>");
	scanf("%s", name);
	//查找一下指定的人是否存在
	int ret = FindByName(pc, name);
	if (ret == -1)
		printf("要查找的人不存在\n");
	else
	{
		printf("%-20s\t%-5s\t%-5s\t%-12s\n", "姓名", "性别", "年龄", "电话");
		printf("%-20s\t%-5s\t%-5d\t%-12s\n", pc->arr[ret].name,
			pc->arr[ret].sex,
			pc->arr[ret].age,
			pc->arr[ret].telephone);
	}
}

//显示联系人
void ShowContact(const struct Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-5s\t%-5s\t%-12s\n", "姓名", "性别", "年龄", "电话");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-5s\t%-5d\t%-12s\n", pc->arr[i].name,
			pc->arr[i].sex,
			pc->arr[i].age,
			pc->arr[i].telephone);
	}
}

//按照名字排
//大家也可以自己写一个使用年龄排序的代码
int cmp_name(const void* e1, const void* e2)
{
	return strcmp(((struct PeoInfo*)e1)->name, ((struct PeoInfo*)e2)->name);//这里排的是升序
}

void SortContact(const struct Contact* pc)
{
	assert(pc);
	qsort(pc->arr, pc->sz, sizeof(pc->arr[0]), cmp_name);
	printf("排序成功!结果如下:\n");
	//在tect.c文件中在调用一次ShowContact函数即可
}

//退出通讯录的最后一定要记得释放空间!!!!!
void Destroy(struct Contact* pc) 
{
	free(pc->arr);
	pc->arr = NULL;
}

最终的代码

下面就给出有关test.c和contact.h的相关代码。
test.c

#include "contact.h"

enum ST
{
	退出通讯录,//0
	增加联系人,//1
	删除联系人,//2
	修改联系人,//3
	查找联系人,//4
	显示联系人,//5
	排序联系人//6
};

void menu()
{
	printf("****************************************\n");
	printf("*****1.增加联系人    2.删除联系人*********\n");
	printf("*****3.修改联系人    4.查找联系人*********\n");
	printf("*****5.显示联系人    6.排序联系人*********\n");
	printf("*****0.退出通讯录               *********\n");
	printf("****************************************\n");
}
int main()
{
	int input = 0;
	//创建通讯录
	struct Contact contact;
	//初始化通讯录数组
	InitContact(&contact);

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 增加联系人:
			ADDContact(&contact);
			system("pause");
			system("cls");
			break;
		case 删除联系人:
			DeleContact(&contact);
			system("pause");
			system("cls");
			break;
		case 修改联系人:
			ModifyContact(&contact);
			system("pause");
			system("cls");
			break;
		case 查找联系人:
			SearchContact(&contact);
			system("pause");
			system("cls");
			break;
		case 显示联系人:
			ShowContact(&contact);
			system("pause");
			system("cls");
			break;
		case 排序联系人:
			//qsort排序
			SortContact(&contact);
			ShowContact(&contact);
			system("pause");
			system("cls");
			break;
		case 0:
			//释放指针-当程序退出的时候,释放掉自己开创的空间,防止内存泄露!!
			Destroy(&contact);
			printf("再见!\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (input);
	
	return 0;
}

contact.h

#include <stdio.h> 
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <windows.h>
#include <stdlib.h>

//方便以后的修改
#define MAX_NAME 30
#define SEX 6
#define TelePhone 13

//联系人信息
struct PeoInfo
{
	char name[MAX_NAME];
	char sex[SEX];
	int age;
	char telephone[TelePhone];
};

//通讯录结构体
struct Contact
{
	//创建sz的目的是为了在进行添加删除等操作时,好找到元素的下标
	int sz;
	struct PeoInfo* arr;
	int capacity;//容量大小.
};

void InitContact(struct Contact* pc);

void ADDContact(struct Contact* pc);

void DeleContact(struct Contact* pc);

void ModifyContact(struct Contact* pc);

void SearchContact(const struct Contact* pc);

void ShowContact(const struct Contact* pc);

void SortContact(const struct Contact* pc);

void Destroy(struct Contact* pc);

热心老铁,在线答疑!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南山忆874

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值