柔性数组:结构体的最后一个元素数组的大小可以是未知数字大小

结构体柔性数组前必须至少有一个其他成员,sizeof返回的结构体大小不包括柔性数组大小

 写了两个代码,第一次用了一次动态内存开辟,第二次用来两次

内存开辟越少越好,调整越少内存碎片越少,内存越连续。

把静态通讯录改进动态内存通讯录,可以想成默认三个人信息每次通信录满时,扩容两个空间(柔性数组)

#define _CRT_SECURE_NO_WARNINGS 1
#define	FAULL_SZ 3
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
enum option//枚举类型菜单选择方便
{
	zero,//0
	add,//1
	del,//2
	search,
	modify,
	show,
	sort,
	save,
};
struct Peoinfo
{
	char name[20];
	int age;
	char sex[5];
	char tele[12];
	char ader[30];
};
struct Contact
{
	struct Peoinfo* data;//存放1000个结构体数据
	int size;//记录当前已有的元素个数
	int capacity;//当前通讯录的最大容量
};
void menu()
{
	printf("****************************************************\n");
	printf("********  1.add             2.del            *********\n");
	printf("********  3.search          4.modify         *********\n");
	printf("********  5.show            6.sort           **********\n");
	printf("********* 7.save            0.exit         *************\n");
	printf("****************************************************\n");
}
int main()
{
	int input;
	void Init_contact(struct Contact* ps);//声明函数
	void Add_contact(struct Contact* ps);//声明函数
	void Show_contact(const struct Contact* ps);//声明函数
	void Del_contact(struct Contact* ps);//声明函数
	void Search_contact(const struct Contact* ps);//声明函数
	void Modify_contact(struct Contact* ps);//声明函数
	void Sort_contact(struct Contact* ps);//声明函数
	void Save_contact(struct Contact* ps);//声明函数
	void Loadcontact(struct Contact* ps);//声明函数
	//struct Peoinfo con[MAX];//存放1000个人的信息
	struct Contact con;//里面包含data指针,size,capacity最大容量
	Init_contact(&con);//初始化通讯录
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case add:
			Add_contact(&con);
			break;
		case del:
			Del_contact(&con);
			break;
		case search:
			Search_contact(&con);
			break;
		case modify:
			Modify_contact(&con);
			break;
		case show:
			Show_contact(&con);
			break;
		case sort:
			Sort_contact(&con);
			break;
		case save:
			Save_contact(&con);
			break;
		case 0://销毁通讯录,释放动态内存
			Save_contact(&con);
			free(con.data);
			con.data = NULL;
			printf("退出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}

	} while (input);
	return 0;
}
void Init_contact(struct Contact* ps)//函数定义
{
	void Loadcontact(struct Contact* ps);
	ps->data =(struct Peoinfo*)malloc(FAULL_SZ * sizeof(struct Peoinfo));
	if (ps->data == NULL)
	{
		return;
	}
	ps->size = 0;
	ps->capacity = FAULL_SZ;
	Loadcontact(ps);
	//把文件中已存在的信息加载到通讯录中

}
void check_capacity(struct Contact* ps)//检查通讯录容量满了没
{
	if (ps->size == ps->capacity)
	{
		//增容
		struct Peoinfo*str=(struct Peoinfo*)realloc(ps->data, (ps->capacity + 2) * sizeof(struct Peoinfo));
		if (str != NULL)
		{
			ps->data = str;
			ps->capacity += 2;
			printf("增容成功\n");
		}
		else
		{
			printf("增容失败\n");
		}
	}
}
void Add_contact(struct Contact* ps)
{
	check_capacity(ps);
	    printf("请输入名字\n");
		scanf("%s", ps->data[ps->size].name);//有了size个元素个数,下一个元素的下标是四则
		printf("请输入年龄\n");
		scanf("%d", &(ps->data[ps->size]).age);//这个用取地址是因为其他数组名也是地址
		printf("请输入性别\n");
		scanf("%s", ps->data[ps->size].sex);
		printf("请输入电话\n");
		scanf("%s", ps->data[ps->size].tele);
		printf("请输入地址\n");
		scanf("%s", ps->data[ps->size].ader);
		ps->size++;
		printf("添加成功\n");
}
void Show_contact(const struct Contact* ps)
{
	if (ps->size == 0)
	{
		printf("通讯录为空\n");
	}
	else
	{
		int i;
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "住址");
		for (i = 0; i < ps->size; i++)
		{

			printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
				ps->data[i].name,
				ps->data[i].age,
				ps->data[i].sex,
				ps->data[i].tele,
				ps->data[i].ader);
		}
	}

}
int find_byname(const struct Contact* ps, char name[20])
{
	int i;
	for (i = 0; i < ps->size; i++)
	{
		if (strcmp(ps->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
void Del_contact(struct Contact* ps)
{
	char name[20];
	printf("请输入你要删除人的名字\n");
	scanf("%s", &name);
	//查找这个要删除的人在什么位置
	/*int i;
	for (i = 0; i < ps->size; i++)
	{
		if (strcmp(ps->data[i].name, name) == 0)
		{
			break;
		}
	}
	if (i == ps->data)
	{
		printf("要删除的这个人不存在\n");
	}*/
	int ret = find_byname(ps, name);//找到了返回元素下标,找不到返回-1

	if (ret != -1)
	{
		//删除这个数据
		int j = 0;
		for (j = ret; j < ps->size - 1; j++)
		{
			ps->data[j] = ps->data[j + 1];
		}
		ps->size--;
		printf("删除成功\n");
	}
	else
	{
		printf("要删除的这个人不存在\n");
	}
}
void Search_contact(const struct Contact* ps)
{
	char name[20];
	printf("请输入你要查找人的名字\n");
	scanf("%s", &name);
	int ret = find_byname(ps, name);
	if (ret == -1)
	{
		printf("查无此人\n");
	}
	else
	{
		printf("%-20s\t%-4s\t%-5s\t%-12s\t%-20s\n", "名字", "年龄", "性别", "电话", "住址");
		printf("%-20s\t%-4d\t%-5s\t%-12s\t%-20s\n",
			ps->data[ret].name,
			ps->data[ret].age,
			ps->data[ret].sex,
			ps->data[ret].tele,
			ps->data[ret].ader);
	}
}
void Modify_contact(struct Contact* ps)
{
	printf("请输入你要修改的名字\n");
	char name[20];
	scanf("%s", &name);
	int ret = find_byname(ps, name);
	if (ret == -1)
	{
		printf("你要修改的人不在通讯录\n");
	}
	else
	{
		printf("请输入名字\n");
		scanf("%s", ps->data[ret].name);
		printf("请输入年龄\n");
		scanf("%d", &(ps->data[ret]).age);
		printf("请输入性别\n");
		scanf("%s", ps->data[ret].sex);
		printf("请输入电话\n");
		scanf("%s", ps->data[ret].tele);
		printf("请输入地址\n");
		scanf("%s", ps->data[ret].ader);
		printf("添加成功\n");
	}
}
int cmp_contact_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}
void Sort_contact(struct Contact* ps)
{
	qsort(ps->data, ps->size, sizeof(ps->data[0]), cmp_contact_by_name);
	printf("排序成功\n");
}
void Save_contact(struct Contact* ps)
{
	FILE* pfwrite = fopen("contact.dat", "wb");
	if (pfwrite == NULL)
	{
		printf("pfwrite:%s\n", strerror(errno));
		return;
	}
	int i;
	for (i = 0; i < ps->size; i++)
	{
		fwrite(&(ps->data[i]), sizeof(struct Peoinfo), 1, pfwrite);
	}
	fclose(pfwrite);
	pfwrite = NULL;
}
void Loadcontact(struct Contact* ps)
{
	struct Peoinfo tmp = { 0 };
	FILE* pfread = fopen("contact.dat", "rb");
	if (pfread == NULL)
	{
		printf("pfread:%s\n", strerror(errno));
	}
	while (fread(&tmp, sizeof(struct Peoinfo), 1, pfread))//0的话没读到文件信息,1的话读到了
	{
		check_capacity(ps);
		ps->data[ps->size] = tmp;
		ps->size++;
	}
	fclose(pfread);
	pfread = NULL;
}

在原通讯录上第二个结构体上加的柔性数组和当前通讯录的最大容量,柔性数组可以用动态内存开辟,这里面增加了文件函数,下节解析