C – 通讯录2.0(详细解析)

2025-04-29 0 584

目录

一.引入枚举

二.动态内存开辟

contact.h

1.初始化

最终代码

2.扩容

最终代码

3.退出自动销毁空间

4.减容

最终代码

三.依姓名排序


通讯录1.0里的 PeoInfo data[MAX] 一次开辟定了内存空间。我放150个人空间不够。只放15个人又太浪费空间。

我们对通讯录1.0进行改造升级。
扩容:方便测试,先给3个内存空间,以后当前联系人数量 == 最大联系人数量时,最大容量增加2个内存空间。
减容:当最大人数比现有人数多5个以上,减容最大人数3个内存空间。



一.引入枚举

test.c 的 switch 语句中的 case 1:   case 2:  ……我们写代码时很容易忘记这些 1,2……分别代表什么含义。
枚举常量的下标默认从0开始,每次增加1 。我们引入枚举常量。
这样在 case x:这里就能看到想要干什么。

test.c

enum Option
{
	EXIT,//0
	ADD, //1
	DEL, //2
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	DELALL
};

switch (input)
{
case ADD:
	AddContact(&con);
	break;
case DEL:
	DelContact(&con);
	break;
case SEARCH:
	SearchContact(&con);
	break;
case MODIFY:
	ModifyContact(&con);
	break;
case SHOW:
	ShowContact(&con);
	break;
case SORT:
	break;
case DELALL:
	DelAllContact(&con);
	break;
case EXIT:
	printf("退出通讯录\\n");
	break;
default:
	printf("输入错误\\n");
	break;
}

二.动态内存开辟

我们要改造,PeoInfo data[MAX] 这里就不能是数组了。

contact.h

//静态版本
typedef struct Contact
{
	PeoInfo data[MAX];//存放人的信息
	int sz;//当前已经存入联系人的个数
}Contact;

我们要用 malloc 开辟一块空间,把地址存起来。
所以,要把 data 改为 PeoInfo * 类型的指针。由 data 指向开辟的空间。data 指向空间里存放人的信息。

除了 data,sz ,还需要记录当前最大容量。

#include <stdlib.h>

#define DEFAULT_SZ 3 //通讯录默认大小
#define INC_SZ 2 //每次增加
#define RED_SZ 3 //每次减少
#define DIFF_SZ_CAPA 5 //最大 - 当前 的差值

//动态版本
typedef struct Contact
{
	PeoInfo* data;//指向存放人信息的空间
	int sz;//当前已经存入联系人的个数
	int capacity;//当前通讯录的最大容量
}Contact;

C – 通讯录2.0(详细解析) 现在data 还没有指向的空间。

1.初始化

contant.c

//静态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, sizeof(pc->data));
}

pc->sz = 0;肯定是不用变的。这里就不能用 memset 了,要用 malloc 开辟内存空间。


我们先开辟空间,再指定通讯录默认大小为3 。这样更合理。

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);
	pc->capacity = DEFAULT_SZ;
}

C – 通讯录2.0(详细解析)

malloc 的参数是要开辟内存的大小(字节)。
所以我们代码中 malloc 参数部分是:一个人的大小 * 通讯录默认人数 = 要开辟字节数

malloc 返回类型 void*  。用 pc 指向的 data 指针接收。data 的类型:PeoInfo *   。所以强制转换。


malloc 是不对开辟的内存空间初始化的。上面这样写,我们要初始化。
所以我用 calloc 。它初始化内存空间的每个字节为0

C – 通讯录2.0(详细解析)  为 num 个大小为 size 的元素开辟内存空间。

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	pc->data = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	pc->capacity = DEFAULT_SZ;
}

同时,我们要对开辟的空间进行维护,判断是否开辟成功。
直接用 pc->data 接收不合适

最终代码

//动态版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact:calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;
}

2.扩容

contact.c

改为动态开辟版本,里面放了元素,放不下时,要考虑扩容问题。对 Add 更改。

//静态版本
void AddContact(Contact* pc)
{
	assert(pc);
	if (pc->sz == MAX)
	{
		printf("通讯录已满,无法添加\\n");
		return;
	}
	//增加一个人的信息
	printf("请输入名字:");......

	pc->sz++;
}

动态版本不存在放满的情况,这里不用判断 == MAX


每次添加联系人后进行判断,当最大空间人数 capacity == 当前空间人数 sz 时,扩容。

//动态版本
void AddContact(Contact* pc)
{
	assert(pc);
	//增加一个人的信息......
	if (pc->sz == pc->capacity)
	{
		//扩容
	}
	pc->sz++;
}

用 realloc 对 data 指向的空间调整。扩容的地方分装成函数。

C – 通讯录2.0(详细解析) 
ptr 是要调整的内存地址   size 是调整后的新大小(字节)。返回开辟好新空间的起始地址。

若当最大空间人数 capacity != 当前空间人数 sz 时,不扩容,直接返回。
新大小(字节) = 增加后的人数 * 每个人的大小。
判断是否成功开辟新空间,若是,再将 ptr 传给 pc->data 

最终代码

void check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//扩容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity:realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;//当前通讯录的最大容量再自增2
	}
}

//动态版本
void AddContact(Contact* pc)
{
	assert(pc);

	//增加一个人的信息
	printf("请输入名字:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入地址:");
	scanf("%s", pc->data[pc->sz].addr);
	printf("请输入电话:");
	scanf("%s", pc->data[pc->sz].tele);

	pc->sz++;
	check_capacity(pc);

	printf("\\n当前联系人数量:%d\\n", pc->sz);
	printf("当前最大容量:%d\\n", pc->capacity);
}

3.退出自动销毁空间

当程序退出时,空间要销毁,free

//test.c
case EXIT:
	DestroyContact(&con);
	printf("退出通讯录\\n");
	break;

//contact.h  销毁内存空间
void DestroyContact(Contact* pc);

//contact.c
void DestroyContact(Contact* pc)
{
	assert(pc);
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	pc = NULL;
}

4.减容

我们还是要判断通讯录是否为空,为空无法删除。
只需在 chack_capacity 函数中加上减容部分,再在 DelContact 函数中加几条语句即可。

最终代码

void check_capacity(Contact* pc)
{
	assert(pc);
	if (pc->sz == pc->capacity)
	{
		//扩容
	}

	if ((pc->capacity - pc->sz) > DIFF_SZ_CAPA)
	{
		//减容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity - RED_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity:realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity -= RED_SZ;
	}
}

//动态版本
void DelContact(Contact* pc)
{
	assert(pc);

	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\\n");
		return;
	}
	//查找
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("要删除的人不存在\\n");
		return;
	}

	int i = 0;
	//删除
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\\n");
	check_capacity(pc);

	printf("\\n当前联系人数量:%d\\n", pc->sz);
	printf("当前最大容量:%d\\n", pc->capacity);
}

三.依姓名排序

qsort 函数。(点击 qsort 查看详细说明)

//contact.c
int sortcon_byname(const void* e1, const void* e2)
{
	return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}

void SortContact_ByName(Contact* pc)
{
	assert(pc);
	qsort(pc->data, pc->sz, sizeof(PeoInfo), sortcon_byname);
	printf("排序成功\\n");
}

//test.c
case SORT:
	SortContact_ByName(&con);
	break;
//contact.h
void SortContact_ByName(Contact* pc);

错误写法:

int sortcon_byname(const void* e1, const void* e2)
{
	return strcmp(((Contact*)e1)->data->name, ((Contact*)e2)->data->name);
}

e1,e2 分别是要比较的两个元素的地址
比较的是人的信息PeoInfo 

我们发现,2.0版本的通讯录,程序关闭,不保存这次运行时Add的联系人。
下次运行,按5显示联系人时,是空白。
怎么解决?

期待通讯录3.0

平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/HDSTQTW/article/details/146609703

遇见资源网 编程语言 C – 通讯录2.0(详细解析) http://www.ox520.com/157910.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务