用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
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。下面详细介绍C语言的基本概念和语法。 1. 变量和数据类型 在C语言,变量用于存储数据,数据类型用于定义变量的类型和范围。C语言支持多种数据类型,包括基本数据类型(如int、float、char等)和复合数据类型(如结构体、联合等)。 2. 运算符 C语言常用的运算符包括算术运算符(如+、、、/等)、关系运算符(如==、!=、、=、<、<=等)、逻辑运算符(如&&、||、!等)。此外,还有位运算符(如&、|、^等)和指针运算符(如、等)。 3. 控制结构 C语言常用的控制结构包括if语句、循环语句(如for、while等)和switch语句。通过这些控制结构,可以实现程序的分支、循环和多路选择等功能。 4. 函数 函数是C语言用于封装代码的单元,可以实现代码的复用和模块化。C语言定义函数使用关键字“void”或返回值类型(如int、float等),并通过“{”和“}”括起来的代码块来实现函数的功能。 5. 指针 指针是C语言用于存储变量地址的变量。通过指针,可以实现对内存的间接访问和修改。C语言定义指针使用星号()符号,指向数组、字符串和结构体等数据结构时,还需要注意数组名和字符串常量的特殊性质。 6. 数组和字符串 数组是C语言用于存储同类型数据的结构,可以通过索引访问和修改数组的元素。字符串是C语言用于存储文本数据的特殊类型,通常以字符串常量的形式出现,用双引号("...")括起来,末尾自动添加'\0'字符。 7. 结构体和联合 结构体和联合是C语言用于存储不同类型数据的复合数据类型。结构体由多个成员组成,每个成员可以是不同的数据类型;联合由多个变量组成,它们共用同一块内存空间。通过结构体和联合,可以实现数据的封装和抽象。 8. 文件操作 C语言通过文件操作函数(如fopen、fclose、fread、fwrite等)实现对文件的读写操作。文件操作函数通常返回文件指针,用于表示打开的文件。通过文件指针,可以进行文件的定位、读写等操作。 总之,C语言是一种功能强大、灵活高效的编程语言,广泛应用于各种领域。掌握C语言的基本语法和数据结构,可以为编程学习和实践打下坚实的基础。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值