【数据结构】:顺序表及其通讯录应用

一.前置知识

1.数据结构

1.1为什么会存在数据结构?

我们常常接触到诸如生活中的姓名、身份证、网页内的图片、视频等各种各样的信息,这些信息就是我们常说的数据。在使用这些数据时,我们发现随着数据的增加,当我们要单独寻找某一个数据时就会非常困难,就像图书馆内书籍如果没有按一定的顺序排放,及时我们知道我们要找的书籍叫什么,我们也无法在浩如烟海的书籍内找到它。

同理,程序中如果不对数据进行管理,可能会导致数据丢失、操作数据困难、野指针等情况。

因此,对于数据,我们需要向像图书馆排列图书一样,采用以一定的形式将数据组织起来,方便日后进行调用。

总结:数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在⼀种或多种特定关系的数据元素的集合。数据结构反映数据的内部构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。

1.2数据结构之线性表

(1)线性表的定义

线性表(linear list)是n个具有相同特性的数据元素的有限序列。具有逻辑上线性,物理存储上不一定连续的特性。 线性表是⼀种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

(2)线性表结构特点
 (a)逻辑结构:逻辑上是线性结构,也就说是连续的⼀条直线。

通过线性表进行组织管理的数据逻辑上呈现由前到后,一对一的线性关系,可以通过如C语言中的结构体与指针来实现该线性逻辑下的一个一个数据的访问,就好像这些数据都是穿在一条线上的一样。

(b)物理结构:物理结构上并不⼀定是连续的(如顺序表物理上连续,链表不连续)

线性表在物理上存储时,通常以数组和链式结构的形式存储。以数组为例,我们都知道数组中每个元素的存储是在内存中开辟一块练习的空间进行存放,元素的地址是连续的,这就是物理结构连续的意思

但是如链式结构,用来存放不同元素的空间时用一块就申请一块,通过指针来实现不同空间的串连,不同空间地址并不是连续的,不具有物理结构上的连续性。

二.顺序表

1.为什么要有顺序表

在C语言中数组其实就是能算一种基本的数据结构,数组有助于我们实现对数组的查找、修改、调用等多种功能。看似功能强大,但其实远远不够。

现在假定数组有10个空间,已经使用了5个,向数组中插入数据步骤:

求数组的长度,求数组的有效数据个数,向下标为数据有效个数的位置插入数据(注意:这里是

否要判断数组是否满了,满了还能继续插入吗).....

再假设数据量非常庞大,频繁的获取数组有效数据个数会还会影响程序执行效率。因此我们需要学习其他数据结构,来提升效率。

而顺序表就在数组的基础上对其进行了封装,在其基础上实现了常用的增删改查等功能接口。

2.静态顺序表的定义

a.特点:

底层创建定长数组来存储元素,为对数组进行增删查改的操作,我们还需要创建一个变量来记录已存放有效的数据个数。

b.缺点:存储空间过大或过小

由于实际生活中,我们往往并不能准确预计实际使用时需要存放数据数量,而且由于静态顺序表底层是使用数组,大小固定,因此往往会出现空间申请过大或过小的情况,如果空间申请过大,那么会白白消耗大量的资源,如果空间申请过小,再对顺序表操作,则会出现数据丢失的情况。上述两种情况都会给公司造成大量的损失。因此,并不推荐屏幕前的读者使用静态顺序表。

3.动态顺序表的定义

a.特点:

动态顺序表定义时,不会直接创建,大小固定的数组,而是创建一个指针来配合之后的realloc来动态申请空间使用。同时由于动态申请空间大小,空间大小时变化的,因此除了需要变量来记录存放的数据个数,我们还需要记录已申请的空间大小。

b.缺点:

1.中间/头部的插入删除,时间复杂度为O(N)

2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

3. 增容⼀般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空。

但相对与静态顺序表较好,推荐使用。

4.实现增删查改功能的封装

(注:基于动态顺序表实现,由于静态顺序表较为简单并且不常使用,笔者便不再过多赘述,后文会给出基于静态顺序表现的通讯录。)

1.顺序表初始化

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

由于我们是使用SLInit来初始化数序表,这就意味着顺序表未初始化,如果函数参数为结构体这里系统就会报错,因为参数为结构体是传值调用,形参是实参的拷贝,但是实参有没有初始化,因此这里便会报错,assert断言防止传入空指针,增强代码韧性,初始时未申请空间,未添加数据指针arr置为NULL,size,capacity赋值0.

2.顺序表的销毁

//销毁
void SLDestroy(SL* ps)
{
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
	ps->size = ps->capacity = 0;
}

我们需要将指针arr指向的realloc申请的空间释放掉,再将野指针arr置为NULL,数据清空,size、capacity置为空。

3.申请空间

#define INIT_CAPACITY 4
//申请空间
void SLCheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity = 0 ? INIT_CAPACITY : 2 * ps->capacity;
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc:");
			exit(1);
		}
		else
		{
			ps->arr = tmp;
			ps->capacity = newcapacity;
		}
	}
}

申请空间前我们需要先判断通过已存储的数据个数与空间个数是否相等来判断空间是否足够,如果

已开辟空间为0;我们先赋予它一个初始大小空间,如果不为0,我们再按每次2倍大小进行扩容(2倍空间扩容是较好选择,具体原因设计数学论证,笔者在此不过多赘述)。申请新空间无误后,我们用arr来指向这片空间。,并将capacity大小更正。

4.数据前插

void SLPushFront(SL* ps, SLDataType data)
{
	assert(ps);
	SLCheckCapacity(ps);
	int i = 0;
	for (i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = data;
	ps->size++;
}

数据前插之前我们通过申请空间函数完成是否申请、申请一系列操作。之后再将原有数据后移一位,完成插入,现有数据+1,size更正。

5.数据尾插

void SLPushBack(SL* ps, SLDataType data)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->arr[ps->size++] = data;
}

先判断空间是否足够,再在最后一个有效数据之后添加数据,更正size。

6.打印顺序表数据

//打印数据
void SLPrint(SL ps)
{
	int i = 0;
	for (i = 0; i < ps.size; i++)
	{
		if (ps.arr[i] >= 'a' && ps.arr[i] <= 'z' || ps.arr[i] >= 'A' && ps.arr[i] <= 'Z')
		{
			printf("%c ", ps.arr[i]);
		}
		else
		{
			printf("%d ", ps.arr[i]);
		}
	}
	printf("\n");
}

笔者这边假设数组内存储的数据是字母或者数字来打印的,其实数据的类型远不是只有这两种,不过大体就是这样,根据存储数据类型来循环打印

7.数据头删

//前删
void SLPopFront(SL* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

前删的话不是真的将数据从数组内移除,我们通过将头数据之后的数据前移,头数据就会被覆盖掉,然后记录数据个数的size-1,这样即使原有arr[size-1]位置还留存尾部的数据,之后我们通过size-1后的size来对数组操作时,留存的数据,我们也访问不到了,就相当于删除了。

8.数据尾删

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	ps->size--;
}

我们只需要size-1,那么跟据size对数组访问时,我们是无法,访问到最后一个数据,也就相当于删除掉了。

9.查找指定数据

//查找数据
int  SLFind(SL ps, SLDataType s)
{
	int i = 0;
	for (i = 0; i < ps.size; i++)
	{
		if (s == ps.arr[i])
		{
			printf("找到了,下标位置为%d\n", i);
			return i;
		}
	}
	if (i == ps.size)
	{
		puts("很遗憾,找不到数据");
		return EOF;
	}
}

我们根据size记录的数据的个数循环访问数组进行比对查找。、

10.数据指定位置插入

//指定位置插入
void SLInsert(SL* ps, int pos, SLDataType data)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	SLCheckCapacity(ps);
	int i = 0;
	for (i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = data;
	ps->size++;
}

插入数据时,我们需先明确插入位置pos,作为控制i的大小的数据,不能为负数,必须小于size(否则会发生数组越界访问)。然后要插入位置及之后的数据向后移动一位,然后将插入位置赋值为要插入的数据。

11.数据指定位置删除

//指定位置删除
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int i = 0;
	for (i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

插入数据时,我们需先明确插入位置pos,作为控制i的大小的数据,不能为负数,必须小于size(否则会发生数组越界访问)。然后要插入位置之后的数据向前移动一位,覆盖要删除的数据,size--,使得无法再访问最后一个多余数据。

5.顺序表的应用:通讯录

C语言基础要求:结构体、动态内存管理、顺序表、文件操作

1.基于动态顺序表实现通讯录

功能要求:

1)至少能够存储100个⼈的通讯信息 2)能够保存用户信息:名字、性别、年龄、电话、地址等

3)增加联系人信息 4)删除指定联系人 5)查找制定联系人 6)修改指定联系人 7)显示联系人信息(8)加载保存历史信息

功能实现:

1.单个联系人信息的集中存储:
#define NAME_MAX 100
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 100
//前置声明
typedef struct SeqList contact;
//用户数据
typedef struct PersonInfo
{
	char name[NAME_MAX];
	char gender[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

我们通过将与联系人相关的姓名、性别、年龄、电话、地址等属性集中在一起创建一个结构体来

集中存储。为方便修改数据,我们使用预处理指令来定义相关量。同时为符合我们的主题,我们将struct SeqList 重新命名为contact。

2.初始化通讯录
//初始化通讯录
void InitContact(contact* con)
{
	assert(con);
	SLInit(con);
}

初始化是通过我们之前封装的初始化功能完成的。

3.添加通讯录数据
int FindByName(contact* con, char* Name)
{
	assert(con);
	assert(Name);
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		if (0 == strcmp(con->arr[i].name, Name))
		{
			return i;
		}
	}
	return EOF;
}
void AddContact(contact* con)
{
	assert(con);
	PeoInfo peniof ;
	puts("请输入要添加的联系人姓名:");
	scanf("%s", peniof.name);
	int find = FindByName(con, peniof.name);
	if (find >= 0)
	{
		puts("您要添加的联系人已存在!");
		ShowContact(con);
		return;
	}	
	puts("请输入要添加的联系人的性别:");
	scanf("%s", peniof.gender);
	puts("请输入要添加的联系人的年龄:");
	scanf("%d", &peniof.age);
	puts("请输入要添加的联系人的电话:");
	scanf("%s", peniof.tel);
	puts("请输入要添加的联系人的地址:");
	scanf("%s", peniof.addr);
	SLPushBack(con, peniof);
	puts("添加成功!");
}

首先我们先创建存放联系人信息的结构体,然后在正式添加数据之前我们需要跟据我们输入的姓名与顺序表中存储的arr[i].name来比较判断通讯录中是否已有该联系人信息,避免重复添加,之后想结构体内每个成员挨个输入,最后再用我们封装的头插或尾插功能插入数据(头插或尾插的选择看个人喜好)。

4.删除通讯录数据
int FindByName(contact* con, char* Name)
{
	assert(con);
	assert(Name);
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		if (0 == strcmp(con->arr[i].name, Name))
		{
			return i;
		}
	}
	return EOF;
}
void DelContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要删除的联系人的姓名");
	scanf("%s",&name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要添加的联系人信息不存在,删除失败");
		return;
	}
	SLErase(con, find);
	puts("删除成功!");
}

在正式添加数据之前我们需要跟据我们输入的姓名与顺序表中存储的arr[i].name来比较判断通讯录中是否已有该联系人信息,避免空删,之后根据名字查找函数,找到要删除联系人信息存储位置,用封装的SLErase函数来删除

5.展示通讯录数据
int FindByName(contact* con, char* Name)
{
	assert(con);
	assert(Name);
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		if (0 == strcmp(con->arr[i].name, Name))
		{
			return i;
		}
	}
	return EOF;
}
void ShowContact(contact* con)
{
	assert(con);
	if(0 == con->size)
	{
		puts("通讯录中未存储任何信息,无法展示");
		return;
	}
	int i = 0;
	for(i = 0;i < con->size;i++)
	{
		printf("联系人%d\n", i + 1);
		printf("姓名:%-s\n", con->arr[i].name);
		printf("性别:%-s\n", con->arr[i].gender);
		printf("年龄:%-d\n", con->arr[i].age);
		printf("电话:%-s\n", con->arr[i].tel);
		printf("地址:%-s\n", con->arr[i].addr);
	}
}

判断通讯录是否存放信息,如果有,挨个循环打印联系人信息

6.查找通讯录数据
void FindContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要查找的联系人的姓名");
	scanf("%s", &name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要查找的联系人信息不存在,查找失败!");
		return;
	}
	puts("查找成功!联系人信息如下:");
	printf("联系人%d\n", find+1);
	printf("姓名:%-s\n", con->arr[find].name);
	printf("性别:%-s\n", con->arr[find].gender);
	printf("年龄:%-d\n", con->arr[find].age);
	printf("电话:%-s\n", con->arr[find].tel);
	printf("地址:%-s\n", con->arr[find].addr);
}

这里笔者是根据姓名查找对应联系人在通讯录中的位置(读者可根据自己喜好设置),然后根据位置访问打印,当然出于用户体验的考量,笔者还根据位置来显示联系人一二三。

7.修改通讯录数据
void contactmenu()
{
	puts("****请选择您要修改的内容****");
	puts("*******1.联系人姓名*******");
	puts("*******2.联系人性别*******");
	puts("*******3.联系人年龄*******");
	puts("*******4.联系人电话*******");
	puts("*******5.联系人地址*******");
	puts("*******0.退出修改 ********");
}
void ModifyContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要修改的联系人的姓名");
	scanf("%s", &name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要添加的联系人信息不存在,修改失败");
		return;
	}
	int num = 0;
	while (1)
	{
		contactmenu();
		scanf("%d", &num);
		if (0 == num)
		{
			puts("修改结束");
			break;
		}
		puts("修改开始!");
		switch (num)
		{
		case 1:
			scanf("%s", con->arr[find].name);
			puts("修改成功");
			break;
		case 2:
			scanf("%s", con->arr[find].gender);
			puts("修改成功");
			break;
		case 3:
			scanf("%d", &con->arr[find].age);
			puts("修改成功");
			break;
		case 4:
			scanf("%s", con->arr[find].tel);
			puts("修改成功");
			break;
		case 5:
			scanf("%s", con->arr[find].addr);
			puts("修改成功");
			break;
		default:
			puts("输入错误请重新输入");
			break;
		}
	}
}

这里修改笔者根据姓名查找通讯录中有无该要修改的联系人,然后笔者设计了一个修改界面,通过while循环与switch选择结构让用户能够自己选择修改什么信息,如果不愿意继续修改则可以直接退出。

8.保存历史通讯录信息
//保存通讯录
void SaveContact(contact* con)
{
	FILE* pf = fopen("contact.txt", "wb");
	if(NULL == pf)
	{
		perror("fopen");
		exit(1);
	}
	int i = 0;
	for(i = 0;i < con->size;i++)
	{
		fwrite(con->arr + i, sizeof(PeoInfo), 1, pf);
	}
	puts("通讯录数据保存成功!");
	fclose(pf);
	pf = NULL;
}

考虑存储联系人数据可能包含各种形式的数据,读取时要考虑多种对应格式较为麻烦,这里笔者采用二进制写入的的方式将输入数据写入到文件中保存起来。

9.加载历史通讯录信息
void LoadContact(contact* con)
{
	assert(con);
	FILE* pf = fopen("contact.txt","rb");
	if(NULL == pf)
	{
		perror("fopen:");
		exit(1);
	}
	PeoInfo info;
	while(fread(&info,sizeof(PeoInfo),1,pf))
	{
		SLPushBack(con, info);
	}
	fclose(pf);
	pf = NULL;
	puts("通讯录历史数据加载成功!");
}

对应保存方式,笔者这里采用二进制读取方式,将单个联系人读取存放到联系人结构体中,再将联系人信息添加到结构体中。

10.销毁通讯录数据
void DestroyContact(contact* con)
{
	assert(con);
	int num = 0;
	scanf("%d",&num);
	puts("是否保存历史数据?");
	puts("0.不是 or 1.是");
	if (num)
	{
		SaveContact(con);
	}
	SLDestroy(con);
	puts("销毁成功!");
}

这里笔者先让用户决定是否保存历史数据,在进行通过动态顺序表封装的销毁功能进行销毁。

用户界面设计代码

void menu()
{
	puts("*********请选择您要进行的操作*******");
	puts("*******1.加载通讯录历史联系人信息****");
	puts("*******2.添加通讯录联系人信息*******");
	puts("*******3.删除通讯录联系人信息*******");
	puts("*******4.展示通讯录联系人信息*******");
	puts("*******5.查找通讯录联系人信息*******");
	puts("*******6.修改通讯录联系人信息*******");
	puts("*************0.退出程序************");
}
int main()
{
	int a = 0;
	contact con;
	InitContact(&con);
	while (1)
	{
		menu();
		scanf("%d", &a);
		if (0 == a)
		{
			break;
		}
		switch (a)
		{
		case 1:
			LoadContact(&con);
			break;
		case 2:
			AddContact(&con);
			break;
		case 3:
			DelContact(&con);
			break;
		case 4:
			ShowContact(&con);
			break;
		case 5:
			FindContact(&con);
			break;
		case 6:
			ModifyContact(&con);
			break;
		default:
			puts("输入错误请重新输入");
			break;
		}
	}
	DestroyContact(&con);
	return 0;
}

2.静态顺序表实现通讯录

注:与动态顺序表实现的通讯录相比较,静态顺序表实现通讯录会出现通许录满情况,添加前应该考虑是否通讯录已满,同时对于用来存放结构体的静态顺序表,由于数组与结构体特性,原本一些初始化会不太可行,因此笔者采用memset内存函数来直接全部“一键”初始化或者销毁。

附上源码:

//stactic_contact.h
#pragma once
#define NAME_MAX 10
#define SEX_MAX 4
#define TEL_MAX 11
#define ADDR_MAX 10
//前置声明
typedef struct SeqList contact;
//通讯录修改菜单
void contactmenu();
//用户数据
typedef struct PersonInfo
{
	char name[NAME_MAX];
	char gender[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}PeoInfo;
//根据名字查找
int FindByName(contact* con, char* Name);
//加载通讯录信息
void LoadContact(contact* con);
//初始化通讯录
void InitContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展示通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
//stactic_seqlist.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#define ARR_MAX 10000
#include#include#include
#include#include"Static_contact.h"
typedef PeoInfo SLDataType;
typedef struct SeqList
{
	SLDataType arr[ARR_MAX];
	int size;
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//申请空间
void SLCheckCapacity(SL* ps);
//前插和尾插
void SLPushFront(SL* ps, SLDataType data);
void SLPushBack(SL* ps, SLDataType data);
//打印数组
void SLPrint(SL ps);
//头删和尾删
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
//static_seqlist.c
//指定位置插入删除
void SLInsert(SL* ps, int pos, SLDataType data);
void SLErase(SL* ps, int pos);
#include"Static_seqlist.h"
//初始化
void SLInit(SL* ps)
{
	assert(ps);
	int i = 0;
	memset(ps->arr, 0, ARR_MAX * (sizeof(SLDataType)));
	ps->size = 0;
}
//销毁
void SLDestroy(SL* ps)
{
	assert(ps);
	memset(ps->arr, 0, ARR_MAX * (sizeof(SLDataType)));
	ps->size = 0;
}
//void SLCheckCapacity(SL* ps)
//{
//	assert(ps);
//	if (ps->size == )
//	{
//		int newcapacity = ps->capacity == 0 ? INIT_CAPACITY : 2 * ps->capacity;
//		SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
//		if (NULL == tmp)
//		{
//			perror("realloc:");
//			exit(1);
//		}
//		else
//		{
//			ps->arr = tmp;
//			ps->capacity = newcapacity;
//		}
//	}
//}
//前插
void SLPushFront(SL* ps, SLDataType data)
{
	assert(ps);
	int i = 0;
	for (i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = data;
	ps->size++;
}
//后插
void SLPushBack(SL* ps, SLDataType data)
{
	assert(ps);
	ps->arr[ps->size++] = data;
}
//void SLPrint(SL ps)
//{
//	int i = 0;
//	for (i = 0; i < ps.size; i++)
//	{
//			printf("%d ", ps.arr[i]);
//	}
//
//
//	printf("\n");
//}
//前删
void SLPopFront(SL* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}
//后删
void SLPopBack(SL* ps)
{
	assert(ps);
	ps->size--;
}
//随机插入
void SLInsert(SL* ps, int pos, SLDataType data)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int i = 0;
	for (i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = data;
	ps->size++;
}
//随机删除
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int i = 0;
	for (i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}
//staric_contact.c
#include"Static_seqlist.h"
void menu()
{
	puts("*********请选择您要进行的操作*******");
	puts("*******1.加载通讯录历史联系人信息****");
	puts("*******2.添加通讯录联系人信息*******");
	puts("*******3.删除通讯录联系人信息*******");
	puts("*******4.展示通讯录联系人信息*******");
	puts("*******5.查找通讯录联系人信息*******");
	puts("*******6.修改通讯录联系人信息*******");
	puts("*************0.退出程序************");
}
void contactmenu()
{
	puts("****请选择您要修改的内容****");
	puts("*******1.联系人姓名*******");
	puts("*******2.联系人性别*******");
	puts("*******3.联系人年龄*******");
	puts("*******4.联系人电话*******");
	puts("*******5.联系人地址*******");
	puts("*******0.退出修改 ********");
}
//根据名字查找
int FindByName(contact* con, char* Name)
{
	assert(con);
	assert(Name);
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		if (0 == strcmp(con->arr[i].name, Name))
		{
			return i;
		}
	}
	return EOF;
}
//初始化通讯录
void InitContact(contact* con)
{
	assert(con);
	SLInit(con);
}
//加载通讯录信息
void LoadContact(contact* con)
{
	assert(con);
	FILE* pf = fopen("contact.txt", "rb");
	if (NULL == pf)
	{
		perror("fopen:");
		exit(1);
	}
	PeoInfo info;
	while (fread(&info, sizeof(PeoInfo), 1, pf))
	{
		SLPushBack(con, info);
	}
	fclose(pf);
	pf = NULL;
	puts("通讯录历史数据加载成功!");
}
//添加通讯录数据
void AddContact(contact* con)
{
	assert(con);
	PeoInfo peniof;
	if((sizeof(PeoInfo) * con->size ) >= ARR_MAX)
	{
		puts("通讯录可添加的名额已满,添加失败!");
		return;
	}
	puts("请输入要添加的联系人姓名:");
	scanf("%s", peniof.name);
	int find = FindByName(con, peniof.name);
	if (find >= 0)
	{
		puts("您要添加的联系人已存在!");
		ShowContact(con);
		return;
	}
	puts("请输入要添加的联系人的性别:");
	scanf("%s", peniof.gender);
	puts("请输入要添加的联系人的年龄:");
	scanf("%d", &peniof.age);
	puts("请输入要添加的联系人的电话:");
	scanf("%s", peniof.tel);
	puts("请输入要添加的联系人的地址:");
	scanf("%s", peniof.addr);
	SLPushBack(con, peniof);
	puts("添加成功!");
}
//删除通讯录数据
void DelContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要删除的联系人的姓名");
	scanf("%s", &name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要添加的联系人信息不存在,删除失败");
		return;
	}
	SLErase(con, find);
	puts("删除成功!");
}
//展示通讯录数据
void ShowContact(contact* con)
{
	assert(con);
	if (0 == con->size)
	{
		puts("通讯录中未存储任何信息,无法展示");
		return;
	}
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		printf("联系人%d\n", i + 1);
		printf("姓名:%-s\n", con->arr[i].name);
		printf("性别:%-s\n", con->arr[i].gender);
		printf("年龄:%-d\n", con->arr[i].age);
		printf("电话:%-s\n", con->arr[i].tel);
		printf("地址:%-s\n", con->arr[i].addr);
	}
}
//查找通讯录数据
void FindContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要查找的联系人的姓名");
	scanf("%s", &name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要查找的联系人信息不存在,查找失败!");
		return;
	}
	puts("查找成功!联系人信息如下:");
	printf("联系人%d\n", find + 1);
	printf("姓名:%-s\n", con->arr[find].name);
	printf("性别:%-s\n", con->arr[find].gender);
	printf("年龄:%-d\n", con->arr[find].age);
	printf("电话:%-s\n", con->arr[find].tel);
	printf("地址:%-s\n", con->arr[find].addr);
}
//修改通讯录数据
void ModifyContact(contact* con)
{
	assert(con);
	char name[NAME_MAX];
	puts("请输入要修改的联系人的姓名");
	scanf("%s", &name);
	int find = FindByName(con, name);
	if (find < 0)
	{
		puts("您要添加的联系人信息不存在,修改失败");
		return;
	}
	int num = 0;
	while (1)
	{
		contactmenu();
		scanf("%d", &num);
		if (0 == num)
		{
			puts("修改结束");
			break;
		}
		puts("修改开始!");
		switch (num)
		{
		case 1:
			scanf("%s", con->arr[find].name);
			puts("修改成功");
			break;
		case 2:
			scanf("%s", con->arr[find].gender);
			puts("修改成功");
			break;
		case 3:
			scanf("%d", &con->arr[find].age);
			puts("修改成功");
			break;
		case 4:
			scanf("%s", con->arr[find].tel);
			puts("修改成功");
			break;
		case 5:
			scanf("%s", con->arr[find].addr);
			puts("修改成功");
			break;
		default:
			puts("输入错误请重新输入");
			break;
		}
	}
}
//保存通讯录
void SaveContact(contact* con)
{
	FILE* pf = fopen("contact.txt", "wb");
	if (NULL == pf)
	{
		perror("fopen");
		exit(1);
	}
	int i = 0;
	for (i = 0; i < con->size; i++)
	{
		fwrite(con->arr + 1, sizeof(PeoInfo), 1, pf);
	}
	puts("通讯录数据保存成功!");
	fclose(pf);
	pf = NULL;
}
//销毁通讯录数据
void DestroyContact(contact* con)
{
	assert(con);
	SaveContact(con);
	SLDestroy(con);
	puts("销毁成功!");
}
//static_function.c
#include"Static_seqlist.h"
int main()
{
	int a = 0;
	contact con;
	InitContact(&con);
	while (1)
	{
		menu();
		scanf("%d", &a);
		if (0 == a)
		{
			break;
		}
		switch (a)
		{
		case 1:
			LoadContact(&con);
			break;
		case 2:
			AddContact(&con);
			break;
		case 3:
			DelContact(&con);
			break;
		case 4:
			ShowContact(&con);
			break;
		case 5:
			FindContact(&con);
			break;
		case 6:
			ModifyContact(&con);
			break;
		default:
			puts("选择错误,请重新选择!");
			break;
		}
	}
	DestroyContact(&con);
	return 0;
}