Bootstrap

address list(dynamic)- 通讯录动态版本

修改一(宏定义 与 结构体成员):



//#define max 1000  // max 使我们一开始就规定好了的 可以存 1000 个 人的信息

                  // 如果 我们想存10个人,那是不是浪费了 可以存储 990 个人 的信息 空间

                  // 有如果,我想存 1001 个人信息,那多出的那一个人,我就无法存入了

                  // 所以 这条语句 我们不需要(删除)

                  

#define SZ 3  // 默认初识容量为 3,被使用的地方,在修改 2 ,容量(capacity)初始化赋值



#define max_name 20

#define max_sex 5

#define max_tele 12

#define max_addr 30  // 方便修改 参数



enum option

{

	Exit,// 0

	Add,// 1

	Del,// 2

	Search,// 3

	Modification,//4

	Show,// 5

	Sort// 6

};



struct people

{

	char name[max_name];

	int age;

	char sex[max_sex];

	char telephone[max_tele];

	char address[max_addr];

};



// 通讯录类型

struct contact

{

	// 这里实现的是一个结构体嵌套



	//struct people data[max];// 那么这里就不能这么写了。



	 struct people* data;    // 有的人可能会说用柔性数组,柔性数组 比 替代方法

	                        // 但是 我们这 需要malloc开辟一次空间,不涉及多次开辟

	                        // 所以这里我们是使用 替代方法

	                        // 创建一个指针 来接收 我们 开辟的动态空间地址

	                        

	int size;// 记录当前已经有的元素个数

};





修改二(初始化修改):


在这里插入图片描述

原来的初始化:


在这里插入图片描述

改进初始化:


在这里插入图片描述

好的,让我们来通过程序注释,来解释一下为什么要这么该


void initcontact(struct contact* ps)

{

	assert(ps);// 判断指针 是否 空指针,原先的忘记加了 == ,勤快的人可以自己加上

	ps->data = (struct people*)malloc(SZ , (sizeof(struct people))); // malloc 开辟一个动态空间,并将其地址赋给 结构体指针变量 data。

	if (ps->data == NULL)// 判断  malloc 函数 开闭动态空间 是否成功

	{

		return;// 不成功,我们什么也做不了(啥事都不干)

	}

	else

	{// 开辟成功

		ps->size = 0;// size 初始化;     size: 记录当前存储了 几个元素

		ps->capacity = SZ;  // 初始化 容量 为 3

	}

}



修改三(函数功能修改):



int main()

{

	int input = 0;



	// 创建一个通讯录

	struct  contact contact;// contact就是通讯录,里面 包含 data 指针, size,capacity



	//初识化 通讯录

	initcontact(&contact);// 把 结构体数组 初始化为 0,只有传址,才能改变 contact 的值

	//



	do

	{

		menu();

		printf("请选择:");

		scanf("%d", &input);

		switch (input)

		{

		case Add:

		

			add_contact(&contact);// 增加一个通讯录信息函数

			                      // 涉及容量更改,实现函数的定义,需做修改

			break;

		case Del:

			del_contact(&contact);//不涉及 容量 更改,不用修改

			break;

		case Search:

			search_contact(&contact);//不涉及 容量 更改,不用修改

			break;

		case Modification:

			modification_contact(&contact);//不涉及 容量 更改,不用修改

			break;

		case Show:

			show_contact(&contact);//不涉及 容量 更改,不用修改

			break;

		case Sort:

			sort_contact(&contact);//不涉及 容量 更改,不用修改

			break;

		case Exit:

		

			Destroy_Contact(&contact);// 开辟了 动态 空间,就会有释放空间

			                           	// 所以在退出通讯录之前,销毁通讯录(释放动态开辟的空间)

			                           	// 故 我们 要在这里加一个 销毁通讯录 的功能

			printf("退出通讯录\n");

			break;

		default:

			printf("选择错误\n");

			break;

		}

	} while (input);

	return 0;

}



修改三(函数功能修改)- add:


原 add 函数实现:

在这里插入图片描述

修改后的 add:


void CheckCapacity(struct contact* ps)

{

	if (ps->capacity == ps->size)// 存储数据的个数 等于 容量,说明容量满了

	{

		// 增容: 使用 realloc 函数 “扩展空间”

		struct people* ptr = (struct people *)realloc(ps->data, (ps->capacity + 2)*sizeof(struct people));

		if (!ptr)// 判断  realloc  "扩展空间"  是否成工本费

		// ptr == NULL == 0 (开辟失败)为假, !(取反) 变为 1 ,即为真,执行 if 语句

		{

			printf("增容失败\n");

		}

		else

		{  //开辟成功

			ps->data = ptr;// 将 realloc 开辟成功 动态空间的地址赋给  结构体指针比阿娘 data

			ps->capacity += 2;// 容量加2

			printf("增容成功\n");

		}

	}

}



void add_contact(struct contact* ps)

{

	assert(ps);

	CheckCapacity(ps);// 检测当前通讯录的容量;1. 如果满了,就增加空间, 2.如果不满,啥事都不干



	// 扩容成功就意味着 -> 现在可以增加数据

	printf("请输入名字:");

	scanf("%s", ps->data[ps->size].name);

	printf("请输入年龄:");

	scanf("%d", &(ps->data[ps->size].age));

	printf("请输入性别:");

	scanf("%s", ps->data[ps->size].sex);

	printf("请输入电话:");

	scanf("%s", ps->data[ps->size].telephone);

	printf("请输入地址:");

	scanf("%s", ps->data[ps->size].address);

	ps->size++; // 存入一个, size 由 0 变为 1

	printf("添加成功\n");

有的人可能会有疑问,如果 调用函数 CheckCapacity(ps); 玩 之后,后面又没有限制条件, 后面的 增加数据的程序 还是会 运行,因此会在想:要不要加个限制条件,或者把它 放进 函数 CheckCapacity(ps) 开辟动态空间成功 语句下。‘’



其实不用,如果 realloc 函数 追加动态空间失败。返回旧地址, 而后面增加数据 部分的 size 在上一次执行完程序后,自增加一,如果此时进行解引用访问 【 ps->data[ps->size].name 】,去赋值。会导致程序 崩溃,就更别提赋值了,



这种错误,看过我 动态分配分配 的 文章的人,此时就应该明白,这是属于 动态内存分配 常见常见错误之一 : 对动态开辟的内存 的 越界访问,



有疑问的朋友,可以返回去看看,重温一篇 动态内存分配 常见的 6个错误,在这里我只提一下,不作解释

1、 对 空指针 的解引用(开辟动态空间失败,返回一个空指针,对其解引用)

2. 对 动态开辟的内存 的 越界访问

3. 重复 释放 同一块 动态开辟的内存

4. 释放 动态开辟的空间 的 一部分

5. 对 非动态 开辟的内存,使用 free

6. 忘记 释放 开辟的动态空间内存,导致 内存泄露 问题



}



修改四(排序功能):


原排序程序:

在这里插入图片描述

;