用C语言动态开辟空间实现通讯录

上节说到,在掌握了指针、结构体。了解了枚举的知识之后,我们可以用C语言写一个通讯录

那其实在实际生活当中,固定大小的通讯录显然并不适用

所以,在掌握了动态开辟内存的函数之后,可以把通讯录改造为一个动态的。

使之能够实现:

1.刚开始只能保存3个联系人信息

2.当保存的联系人信息等于3个的时候,再增加可以保存2个联系人信息的空间

(这个默认和调整的大小是可以手动设置的)

3.每次增加的空间可以保存2个联系人信息

 与静态开辟的比较就是这样

那相对于静态实现通讯录,代码也有所不同:

#define _CRT_SECURE_NO_WARNINGS 1

#include"contact.h"

void menu()
{
	printf("*****************************\n");
	printf("***  1.add      2.delete ****\n");
	printf("***  3.modify   4.find   ****\n");
	printf("***  5.sort     6.print  ****\n");
	printf("***  0.exit              ****\n");
	printf("*****************************\n");
}


void test()
{
	int input = 0;
	
	Contact TXL;
	
	//初始化
	InitContact(&TXL);

	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddLianxiren(&TXL);
			break;
		case DELETE:
			DelLianxiren(&TXL);
			break;
		case MODIFY:
			ModifyLianxiren(&TXL);
			break;
		case FIND:
			FindLianxiren(&TXL);
			break;
		case SORT:
			LianxirenSort(&TXL);
			break;
		case PRINT:
			PrintXinXi(&TXL);
			break;
		case EXIT:
			DestoryTXL(&TXL);
			printf("退出成功!\n");
			break;
		default:
			printf("输入有误,请重新输入!\n");
			break;
		}

	} while (input);
}

int main()
{
	test();
}

在test.c中,因为是动态内存开辟,所以在使用完成之后,需要将申请的空间给free掉

所以这里只有在销毁通讯录的时候多了一个销毁函数

再来看contact.h

#define _CRT_SECURE_NO_WARNINGS 1

//#define的放置
#define MAX_SIZE 10
#define MAX_NAME 20
#define MAX_SEX 10
#define MAX_TELE 12
#define MAX_ADDR 30

//动态
#define DEFAULT_SIZE 3

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


enum choose
{
	EXIT,
	ADD,
	DELETE,
	MODIFY,
	FIND,
	SORT,
	PRINT
};

//创建出来结构体
//联系人信息:姓名,性别,年龄,电话,住址
typedef struct PeoInFo
{
	char name[20];
	char sex[10];
	int age;
	char tele[12];
	char addr[30];
}PeoInFo;

//创建出通讯录
//typedef struct Contact
//{
//	PeoInFo data[MAX_SIZE];
//	int sz;
//}Contact;
//动态
typedef struct Contact
{
	PeoInFo* data;
	int sz;
	int capacity;
}Contact;

//初始化通讯录
void InitContact(Contact*pc);

//增加
void AddLianxiren(Contact* pc);

//打印
void PrintXinXi(Contact* pc);

//删除
void DelLianxiren(Contact* pc);

//查
void FindLianxiren(Contact* pc);

//改
void ModifyLianxiren(Contact* pc);

//排序
void LianxirenSort(Contact*pc);

//销毁
void DestoryTXL(Contact* pc);

可以看到,我们在创建结构体的时候,结构体成员的设置发生了变化

1.第一个是PeoInFo类型的指针,指向我们申请的那片空间

2.第二个就是现在保存了多少个联系人信息

3.第三个就是当前申请的空间能够保存多少个联系人的信息。

最重要的是在contact.c中的实现:

我用几个截出来的代码进行讲解:

首先是初始化:

然后就是在判断空间用完,增加空间的地方:

 在销毁这里,也有些不同:

 然后下面就是contact.c的代码了。

#define _CRT_SECURE_NO_WARNINGS 1

//联系人信息:姓名,性别,年龄,电话,住址

#include"contact.h"

//初始化通讯录
//void InitContact(Contact* pc)
//{
//	assert(pc);
//	pc->sz = 0;
//	memset(pc->data, 0, MAX_SIZE * sizeof(PeoInFo));
//}
//动态
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	pc->data = (PeoInFo*)malloc(sizeof(PeoInFo) * DEFAULT_SIZE);
	pc->capacity = DEFAULT_SIZE;
	if (pc->data == NULL)
	{
		perror("InitContact:malloc");
		return;
	}
	memset(pc->data, 0, sizeof(PeoInFo) * DEFAULT_SIZE);
}


//空间不足,动态增容
void check_capacity(Contact* pc)
{
	PeoInFo* tmp = (PeoInFo*)realloc(pc->data,sizeof(PeoInFo) * (pc->capacity + 2));
	if (tmp == NULL)
	{
		perror("check_capacity:realloc");
		return;
	}
	pc->capacity += 2;
	pc->data = tmp;
	printf("增容成功!\n");
}


//增加联系人信息
void AddLianxiren(Contact* pc)
{
	assert(pc);
	/*if (pc->sz == MAX_SIZE)
	{
		printf("空间不足!\n");
		return;
	}*/
	//动态
	if (pc->sz == pc->capacity)
	{
		check_capacity(pc);
	}

	printf("请输入联系人姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入联系人性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入联系人年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入联系人电话:");
	scanf("%s", pc->data[pc->sz].tele);
	printf("请输入联系人住址:");
	scanf("%s", pc->data[pc->sz].addr);
	pc->sz++;
	printf("添加成功!\n");
}

//问题1——忘记const
int find_by_name(const Contact* pc, char name[MAX_NAME])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(pc->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}


//删除信息
void DelLianxiren(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("无可删除信息\n");
		return;
	}
	char ret[MAX_NAME] = {0};
	printf("请输入要删除联系人的名字:");
	scanf("%s", ret);
	int XiaBiao = find_by_name(pc,ret);
	if (XiaBiao == -1)
	{
		printf("通讯录中没有该联系人的信息\n");
		return;
	}
	int i = 0;
	for(i=XiaBiao;i<pc->sz-1;i++)
	{
		pc->data[i] = pc->data[i + 1];				//注意这里有点懵
	}
	pc->sz--;
	printf("删除成功\n");
}


//打印信息
void PrintXinXi(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("无联系人信息\n");
	}
	printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名","性别", "年龄", "电话号码", "家庭住址");
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s", pc->data[i].name);
		printf("%-10s", pc->data[i].sex);
		printf("%-5d", pc->data[i].age);
		printf("%-12s", pc->data[i].tele);
		printf("%-30s\n", pc->data[i].addr);
	}
}

//查
void FindLianxiren(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("通讯录中无联系人信息!\n");
	}
	char ret[MAX_NAME] = { 0 };
	printf("请输入要查找联系人的名字:");
	scanf("%s", ret);
	int XiaBiao = find_by_name(pc, ret);
	if (XiaBiao == -1)
	{
		printf("没有该联系人的信息!\n");
		return;
	}
	printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名", "性别", "年龄", "电话号码", "家庭住址");
	
	printf("%-20s", pc->data[XiaBiao].name);
	printf("%-10s", pc->data[XiaBiao].sex);
	printf("%-5d", pc->data[XiaBiao].age);
	printf("%-12s", pc->data[XiaBiao].tele);
	printf("%-30s\n", pc->data[XiaBiao].addr);
	return;
}

//改
void ModifyLianxiren(Contact* pc)
{
	assert(pc);
	if (pc->sz == 0)
	{
		printf("没有联系人信息可修改!\n");
		return;
	}
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改联系人的名字:");
	scanf("%s", name);
	int ret = find_by_name(pc, name);
	if (ret == -1)
	{
		printf("没有您要修改的联系人信息!\n");
	}
	printf("请输入联系人姓名:");
	scanf("%s", pc->data[ret].name);
	printf("请输入联系人性别:");
	scanf("%s", pc->data[ret].sex);
	printf("请输入联系人年龄:");
	scanf("%d", &(pc->data[ret].age));
	printf("请输入联系人电话:");
	scanf("%s", pc->data[ret].tele);
	printf("请输入联系人住址:");
	scanf("%s", pc->data[ret].addr);
	printf("修改成功!\n");

	printf("%-20s%-10s%-5s%-12s%-30s\n", "姓名", "性别", "年龄", "电话号码", "家庭住址");

	printf("%-20s", pc->data[ret].name);
	printf("%-10s", pc->data[ret].sex);
	printf("%-5d", pc->data[ret].age);
	printf("%-12s", pc->data[ret].tele);
	printf("%-30s\n", pc->data[ret].addr);
}

int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((PeoInFo*)e1)->name, ((PeoInFo*)e2)->name);
}
int cmp_by_sex(const void* e1, const void* e2)
{
	return strcmp(((PeoInFo*)e1)->sex, ((PeoInFo*)e2)->sex);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((PeoInFo*)e1)->age - ((PeoInFo*)e2)->age;
}
int cmp_by_tele(const void* e1, const void* e2)
{
	return strcmp(((PeoInFo*)e1)->tele, ((PeoInFo*)e2)->tele);
}
int cmp_by_addr(const void* e1, const void* e2)
{
	return strcmp(((PeoInFo*)e1)->addr, ((PeoInFo*)e2)->addr);
}

//排序
void LianxirenSort(Contact* pc)
{
	assert(pc);
	int n = 0;
	printf("选择您想要的排列方式:\n");
	printf("*********************************\n");
	printf("***  1.姓名      2.性别      ****\n");
	printf("***  3.年龄      4.电话号码  ****\n");
	printf("***  5.家庭住址              ****\n");
	printf("*********************************\n");
	scanf("%d", &n);

	while(1)
	{
		switch (n)
		{
		case 1:
			qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_name);
			PrintXinXi(pc);
			break;
		case 2:
			qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_sex);
			PrintXinXi(pc);
			break;
		case 3:
			qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_age);
			PrintXinXi(pc);
			break;
		case 4:
			qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_tele);
			PrintXinXi(pc);
			break;
		case 5:
			qsort(pc->data, pc->sz, sizeof(PeoInFo), cmp_by_addr);
			PrintXinXi(pc);
			break;
		default:
			printf("输入错误请重新输入!\n");
		}
	}
}


//销毁
void DestoryTXL(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	printf("销毁成功!\n");
}

总结:

总的来说,这个我理解起来还是有难度的,

1.最主要的不同就是在静态实现通讯录的时候,我们创建的结构体中就包含了整个通讯录的空间

但是在动态实现通讯录的时候,我们实际上只是创建了一个仅有3个变量的结构体

最主要的是靠着那个指针来实现的各种操作

2.然后就是我经常会忘记使用assert来断言指针,还有const也经常忘记使用

在自己实现了动态申请空间的通讯录之后,真正的感受到了动态申请,动态管理是怎么一回事了。

作者水平欠佳,文章恐有疏漏,还望读者指正。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值