手把手教你写通讯录(含动态版)

目录

一、框架

二、实现

1.初始化通讯录

2.增加联系人

3.打印通讯录

4.删除联系人

5.修改联系人

6.查找联系人

7.退出通讯录

8.拓展:通讯录排序

 9.全代码

三、动态版

1.结构体修改

2.初始化修改 

3.扩容实现

 4.善后函数

 5.全代码


一、框架

实现通讯录之前,我们要想一下,我们这个通讯录需要有什么功能。从手机自带的通讯录借鉴,通讯录的功能首先要能存放联系人的各种信息如:姓名,性别,年龄,联系方式,地址等等。此次要在这个基础上实现对通讯录存放的联系人的删除,查找,修改等等。

思路有了,那么我们先来打一个框架    先用printf实现一个菜单    这个菜单可以帮助我们更方便地使用通讯录,可理解为指引功能。接着,功能的选择,我们可以用switch实现   其次,这个通讯录,我们想让它一直进行下去,直到使用者不想用了,所以写一个循环    综上所述,代码实现我们可以写成这般

#include<stdio.h>
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******0.退出通讯录*********\n");
		printf("**********************************************\n");
		printf("**********************************************\n");
}
int main()
{
	int choose = 0;
	do
	{
        menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			//调用增加联系人的函数
			break;
		case 2:
			//调用删除联系人的函数
			break;
		case 3:
			//调用修改联系人信息的函数
			break;
		case 4:
			//调用查找联系人的函数
			break;
        case 5:
             //调用打印通讯录的函数
            break;
		case 0:
			//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

 功能还未具体实现,只是个框架,接着我们想一想,联系人不可能只有一个,会有很多个  那么我们应该要用数组存放,而联系人所包含的信息也很多   有姓名,年龄,性别,联系方式,地址等等,一个普通的数组根本放不下,而创建多个数组分别存放又显得非常繁琐。所以我们应该创建一个联系人结构体   这样就能创建出对应的结构体数组,即联系人数组,操作起来就会方便很多。

#include<stdio.h>
struct people
{
	char name[10];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[5];//与名字的原因相同
	char tel[12];//与名字的原因相同
	char address[15];//与名字的原因相同
};

但是,如果未来我们想要一个能存放15个元素的名字,我们就得从头把10给改到尾,非常不方便,所以这里采用了宏定义的方式,定义了几个全局常量,这样便能实现牵一发而动全身

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
};

继续思考,联系人的结构体被定义完了,我们接下来是不是就应该创建一个对应的结构体数组然后思考怎么对着这个结构体数组增删改差啊,差不多到这一步了。但我们的框架还差一点。一开始我们的通讯录肯定是一个联系人都没有的,我们要往里边存放联系人,而每当你存放一个联系人就会使对应的联系人数组里边的联系人增多一个,下次再存放联系人的时候,对应数组的下标肯定要往后波动一个,我们是否可以创建一个变量专门来存放存放了多少个联系人   一开始没有联系人,那么这个变量就为0,对应数组第一个元素的下标,进行存放操作时就直接取它,而存放完一个后,变量就+1,对应下一次要存放的目标下标。可是单独创建一个这样的变量,到时函数传参的时候一定会多一个步骤   似乎不是很方便,要是我们传一个变量,这个变量不单单是联系人数组,而是联系人数组和已存放联系人的数量就好了   所以我们可以再创建一个叫通讯录的结构体,把这两个都包含进去   如此便可以实现在调用函数传参时传一个就可以实现我们需要的功能。

#include<stdio.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 100//最多能存放100个联系人
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
typedef struct contact
{
	peo data[people_max];
	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
	int sz;
	//已经存放了几个联系人
}con;//别名,使用方便

框架到这里基本就打好了,接下来便是实现

二、实现

1.初始化通讯录

在做一切操作之前我们都应该先创建一个通讯录类型(我们之前创建的那个结合了成员数组和存放个数)的变量,然后对它初始化,可以把初始化通讯录这个功能单独分装成一个函数,我们这个函数的返回类型笔者给的是空类型的,因为我们只是用它来初始化通讯录,当然你想的话可以给它的返回类型设计成通讯录的指针变量,这样到时就可以实现返回通讯录的首地址,实现链式法则,进行操作。

void init_contact(con* c1)//传址调用,实现修改
{
	assert(c1);
	//断言,避免传空指针,使用这个函数需要引assert.h头文件
	memset(c1->data,0,sizeof(c1->data));
	//c1->data,指向的是数组名,代表着数组首元素的地址
	//sizeof(c1->data)代表计算整个数组所占的字节数
	//0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	//memset位于stdlib.h头文件中
	c1->sz = 0;//把c1指向的sz初始化为0
}

2.增加联系人

初始化完联系人,我们的所有前置准备动作算是终于做完,接下来就讲一下如何添加联系人。其实很简单,下标有了(sz,也就是当前通讯录存放的联系人个数)自然就能找到被操作的目标。通过箭头访问结构体中的成员    再通过sz找到目标,找到目标之后,使用scanf对它们修改就行   要注意的一点就是,当我们增加联系人的时候,通讯录已经放满了很显然就不能再放了,再放就属于是越界访问了所以我们应该来个判断,当存放的联系人达到上限了就别存了。

void add(con*c1)
{
	assert(c1);//断言防止传空指针
	if (c1->sz == people_max)
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}

写好的函数别忘了放在switch语句中调用 

3.打印通讯录

实现增加联系人的功能之后你肯定会感觉怪怪的,我这添加了跟没添加一样,我又看不到,那么我们就一起来实现一个打印通讯录的功能,这样就能够看到我们添加进去的信息了,先思考返回类型,只是打印通讯录的内容,没有做别的操作,继续用空类型的,参数部分还是传通讯录结构体变量指针(有这个东西,通讯录的所有内容都可以找的出来),如此便已经可以实现我们的功能了,但我们可以设计的更完美一些,我们的目标只是打印通讯录的内容,并不会对内容进行修改,故我们可以在*号前加一个const修饰,这样通讯录结构体变量指针所指向的内容就不可能被修改了。

void print_contact(const con* c1)
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}

写好的函数别忘了放到switch语句中调用  

4.删除联系人

删除联系人,我们一样的返回类型一样采用空类型参数一样是通讯录结构体变量的指针,但由于要修改,故不用const修饰。我们可以通过输入姓名的方式,再通过strcmp和循环,循环次数显然是当前有多少个联系人就循环几次,相当于是把通讯录遍历一遍来寻找是否存在目标人物,存在就把它对应的下标存储起来,不存在就直接返回就行。找到目标后,下一步就是删除,删除的话可以通过覆盖的方式来删除,比方说我要删除的这个联系人的下标为2即第三个元素,而我已经存放了5个成员,那么我们就将第四个元素覆盖到第三个元素上,将第五个元素覆盖到第四个元素上,再对sz--就可以实现删除,理论存在,开始实践

void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	char a1[name_max] = { 0 };//初始化
	printf("请输入你要删除联系人的姓名\n");
	scanf("%s", &a1);
	int i = 0; int flaw = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			flaw = 1;//用来分析是否查找到目标
			break;//找到下标,退出循环
		}
	}
	if (flaw == 0)
	{
		printf("查无此人,删除失败\n");
		return;
	}
	for (; i < c1->sz-1; i++)
	//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
    //因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
	//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
	{
		c1->data[i] = c1->data[i+1];
	}
	printf("删除成功\n");
	c1->sz--;
}

 实现完这个部分之后,笔者又觉得这个查找目标下标的功能啊,它很方便,很有用啊,到时我们做查找并打印功能时就可以用到,于是我们把这部分功能分装成一个函数,让它通过姓名的方式为我们查找目标的下标

int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{	
    assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}

5.修改联系人

修改联系人就简单了,有了之前那个寻找对应联系人的函数,我们已经能够快速的找到目标下标,也就是说,我们只要对着相应的东西改就行

void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}

6.查找联系人

这个用我们的下标查找函数,找到下标再多一步打印即可

void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}

7.退出通讯录

最简单的来了,exit()函数即可

8.拓展:通讯录排序

我们存放联系人的时候很可能是随便来的,就是想到谁就放谁,久而久之,通讯录就会变得杂乱无章,因此这里笔者特意写了一个通讯录排序的功能,这里面使用到了qsort函数,如果你对qsort函数不是很理解的话那可以去看笔者之前写的文章手把手教你使用qsort函数http://t.csdn.cn/wSMoW

别忘了将函数放进switch语句中,并重新规划菜单指引操作。 

基础款

int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
void sort(const con*c1)
{
	qsort(c1->data,c1->sz, sizeof(peo),cmp_age);
	//c1->data是存放联系人的结构体数组首元素的地址
	//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
	//cmp_age是需要我们自己设计的函数
	//如何设计看笔者之前的文章,这里就不再赘述
	printf("排序成功\n");
}

 升级版

int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}

 9.全代码

全都被笔者写在了一个文件中,其实最好还是分一下,比如说一个文件放函数的声明,一个文件来实现函数,一个文件来实现操作

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 100//最多能存放100个联系人
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
typedef struct contact
{
	peo data[people_max];
	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
	int sz;
	//已经存放了几个联系人
}con;//别名,使用方便
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******6.通讯录排序*********\n");
		printf("*******7.退出通讯录***************************\n");
		printf("**********************************************\n");
}
void init_contact(con* c1)//初始化通讯录
{
	assert(c1);
	//断言,避免传空指针,使用这个函数需要引assert.h头文件
	memset(c1->data,0,sizeof(c1->data));
	//c1->data,指向的是数组名,代表着数组首元素的地址
	//sizeof(c1->data)代表计算整个数组的字节数
	//0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	//memset位于stdlib.h头文件中
	c1->sz = 0;//把c1指向的sz初始化为0
}
void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if (c1->sz == people_max)
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}
void print_contact(const con* c1)//打印通讯录
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}
int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}
void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}
int main()
{
	int choose = 0;
	con c1;
	init_contact(&c1);
	do
	{
	    menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			add(&c1);//调用增加联系人的函数
			break;
		case 2:
			con_del(&c1);//调用删除联系人的函数
			break;
		case 3:
			dif(&c1);//调用修改联系人信息的函数
			break;
		case 4:
			search(&c1);//调用查找联系人的函数
			break;
		case 5:
			print_contact(&c1);//调用打印通讯录的函数
			break;
		case 6:
			sort(&c1);//调用排序函数
			break;
		case 0:
			exit(1);//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

三、动态版

1.结构体修改

动态版本我们通过malloc,realloc和free等一系列动态内存管理函数来实现,如果你对这方面不是很了解的话你可以参考一下笔者之前的文章手把手教你玩转内存函数http://t.csdn.cn/2hntB​​​​​​

首先要修改的是我们的结构体成员, 我们之前采用的是数组的方式实现成员的多少的,但在c环境下数组的定义要求是常量,才能创建数组因此它是不可变的现在我们采用指针的方式来实现一个能随着我们的需要动态分配内存的“数组”,再然后就是容量的大小很重要,它可以判断是否需要扩容

typedef struct contact
{
	peo* data;
	//使用指针的方式
	int sz;
	//已经存放了几个联系人
	int capacity;
	//总容量的大小
}con;//别名,使用方便

2.初始化修改 

void init_contact(con* c1)//初始化通讯录
{

	c1->sz = 0;
	c1->capacity = people_max;
	c1->data =(peo*)malloc(sizeof(peo)*people_max);
	//开辟数组
	if (c1->data == NULL)
	{
		perror("init_contact");
		//开辟失败,错误提示
		exit(1);
	}
}

3.扩容实现

void if_bigger(con* c1)
{
	if (c1->capacity == c1->sz)
   //当容量和存放的个数相等,空间不够,得扩容
	{
		c1->data = (peo*)realloc(c1->data,sizeof(peo)*(c1->capacity + 2));
		//扩容之后比之前的容量大2,按自己喜欢调节
		if (c1->data == NULL)
		{
			perror("if_bigger");
			//开辟失败,错误提示
			return;
		}
		else
		{
			c1->capacity += 2;//开辟成功容量+2
			printf("开辟成功,当前可存放联系人个数为%d\n", c1->capacity);
		}
	}
}

在增加联系人那里添加

void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if_bigger(c1);//放在前面,每次来都先检测一下容量是否够
	if (c1->sz == c1->capacity)
 //这个也要修改,现在是和容量比,之前是和people_max比
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}

这里笔者把people_max调成了3使初始容量减少,这样方便测试扩容效果

 4.善后函数

这个东西是用来摧毁通讯录的,放在退出那块就行

void destroy_contact(con*c1)
{
	c1->capacity = 0;
	c1->sz = 0;
	free(c1->data);//释放空间
	c1->data = NULL;//野指针变空指针
}

 5.全代码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define name_max 10
#define sex_max 5
#define tel_max 12
#define address_max 15
#define people_max 3
typedef struct people
{
	char name[name_max];//姓名存放的是字符串,故用char
	int age;     //年龄存放的是一个数字,故用int
	char sex[sex_max];//与名字的原因相同
	char tel[tel_max];//与名字的原因相同
	char address[address_max];//与名字的原因相同
}peo;//来个别名,使用方便
//typedef struct contact
//{
//	peo data[people_max];
//	//结构体成员名为data,它是可以存放100个struct people类型元素的数组
//	int sz;
//	//已经存放了几个联系人
//}con;//别名,使用方便
typedef struct contact
{
	peo* data;
	//使用指针的方式
	int sz;
	//已经存放了几个联系人
	int capacity;
	//总容量的大小
}con;//别名,使用方便
void destroy_contact(con*c1)
{
	c1->capacity = 0;
	c1->sz = 0;
	free(c1->data);//释放空间
	c1->data = NULL;//野指针变空指针
}
void menu()
{
		printf("**********************************************\n");
		printf("**********************************************\n");
		printf("*******1.增加联系人******2.删除联系人*********\n");
		printf("*******3.修改联系人******4.查找联系人*********\n");
		printf("*******5.打印通讯录******6.通讯录排序*********\n");
		printf("*******7.退出通讯录***************************\n");
		printf("**********************************************\n");
}
void init_contact(con* c1)//初始化通讯录
{
	//assert(c1);
	断言,避免传空指针,使用这个函数需要引assert.h头文件
	//memset(c1->data,0,sizeof(c1->data));
	c1->data,指向的是数组名,代表着数组首元素的地址
	sizeof(c1->data)代表计算整个数组的字节数
	0代表把给定地址往后的sizeof(c1->data)个字节都初始化为0
	memset位于stdlib.h头文件中
	//c1->sz = 0;//把c1指向的sz初始化为0
	c1->sz = 0;
	c1->capacity = people_max;
	c1->data =(peo*)malloc(sizeof(peo)*people_max);
	//开辟数组
	if (c1->data == NULL)
	{
		perror("init_contact");
		//开辟失败,错误提示
		exit(1);
	}
}
void if_bigger(con* c1)
{
	if (c1->capacity == c1->sz)//容量和存放的个数相等,显然空间不够
	{
		c1->data = (peo*)realloc(c1->data,sizeof(peo)*(c1->capacity + 2));
		//扩容之后比之前的容量大2
		if (c1->data == NULL)
		{
			perror("if_bigger");
			//开辟失败,错误提示
			return;
		}
		else
		{
			c1->capacity += 2;//开辟成功容量+2
			printf("开辟成功,当前可存放联系人个数为%d\n", c1->capacity);
		}
	}
}
void add(con*c1)//增加联系人
{
	assert(c1);//断言防止传空指针
	if_bigger(c1);//放在前面,每次来都先检测一下容量是否够
	if (c1->sz == c1->capacity)//这个也要修改,现在是和容量比
	{
		printf("通讯录已满,存放失败\n");
		//存放失败就直接返回,由于是空类型,所以直接return
		return;
	}
	else//通讯录没满就往里存
	{
		printf("请输入联系人姓名\n");
		scanf("%s",c1->data[c1->sz].name);//字符串变量名就是首地址
		printf("请输入联系人年龄\n");
		scanf("%d", &(c1->data[c1->sz].age));//年龄是整型,得取地址
		printf("请输入联系人性别\n");
		scanf("%s", c1->data[c1->sz].sex);
		printf("请输入联系人联系方式\n");
		scanf("%s", c1->data[c1->sz].tel);
		printf("请输入联系人家庭住址\n");
		scanf("%s", c1->data[c1->sz].address);
		printf("添加成功\n");
		c1->sz += 1;//联系人增多一名
	}
}
void print_contact(const con* c1)//打印通讯录
{
	assert(c1);//断言避免传空指针
	int i = 0;
	if (c1->sz == 0)
	{
		printf("通讯录未存放联系人\n");
		return;
	}
	printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
	//根据自己的喜好对齐,使信息更加明了
	for (i = 0; i < c1->sz; i++)//写个循环,因为可能不止一个联系人
	{
		printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
			c1->data[i].name,
			c1->data[i].age,
			c1->data[i].sex,
			c1->data[i].tel,
			c1->data[i].address);
	}
}
int find(const con* c1)
//要有返回值来判断是否找到目标
//只是寻找不修改,用const修饰
{
	char a1[name_max] = { 0 };//初始化
	scanf("%s", &a1);
	int i = 0;
	for (i = 0; i < c1->sz; i++)
	{
		if (strcmp(a1, c1->data[i].name) == 0)
		{
			return i;//找到目标,直接返回下标,注意下标可能为0
		}
	}
	return -1;//找不到目标,返回-1
}
void con_del(con* c1)//删除联系人
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	printf("请输入你要删除的联系人的姓名\n");
	int i = find(c1);
	if (i!=-1)
	{//找到的话返回非0的数,为真,进入删除,没找到返回0,为假,不进入删除
		for (; i < c1->sz - 1; i++)
			//为什么是sz-1呢,避免越界操作,最后一个元素其实我们是可以不用管它的
			//因为sz--之后你下一次增加联系人的时候就会把它覆盖掉
			//且你不增加联系人的话,你肯定再也访问不到这个下标对应的元素了
		{
			c1->data[i] = c1->data[i + 1];
		}
		printf("删除成功\n");
		c1->sz--;
	}
	else
	{
		printf("查无此人,操作失败\n");
		return;
	}
}
void dif(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else {
		printf("请输入你要修改的联系人的姓名\n");
		int i = find(c1);
		if (i != -1)
		{
			printf("请输入被修改后的姓名\n");
			scanf("%s", c1->data[i].name);
			printf("请输入被修改后的年龄\n");
			scanf("%d", &c1->data[i].age);
			printf("请输入被修改后的性别\n");
			scanf("%s", c1->data[i].sex);
			printf("请输入被修改后的联系方式\n");
			scanf("%s", c1->data[i].tel);
			printf("请输入被修改后的地址\n");
			scanf("%s", c1->data[i].address);
			printf("修改成功\n");
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
void search(con* c1)
{
	assert(c1);
	if (c1->sz == 0)
	{
		printf("通讯录为空,操作失败\n");
		return;
	}
	else
	{
		printf("请输入你要查找的联系人姓名\n");
		int i=find(c1);
		if(i!=-1)
		{
			printf("%-10s\t%-5s\t%-5s\t%-12s\t%-15s\t\n", "姓名", "年龄", "性别", "联系方式", "家庭住址");
			printf("%-10s\t%-5d\t%-5s\t%-12s\t%-15s\t\n",
				c1->data[i].name,
				c1->data[i].age,
				c1->data[i].sex,
				c1->data[i].tel,
				c1->data[i].address);
			return;
		}
		else
		{
			printf("查无此人,操作失败\n");
			return;
		}
	}
}
int cmp_age(const void*ptr1,const void*ptr2 )
{
	return ((peo*)ptr1)->age - ((peo*)ptr2)->age;//升序排序
}
int cmp_name(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->name,((peo*)ptr2)->name);
}
int cmp_tel(const void* ptr1, const void* ptr2)
{
	return strcmp(((peo*)ptr1)->tel,((peo*)ptr2)->tel);
}
void sort(const con* c1)
{
	int choose = 0;
	do {
		printf("输入0退出排序\n");
		printf("输入1按照年龄排序\n");
		printf("输入2按照姓名排序\n");
		printf("输入3按照电话号码排序\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_age);
			//c1->data是存放联系人的结构体数组首元素的地址
			//c1->sz是要排的人数//sizeof(peo)为联系人结构体数组一个元素的大小
			//cmp_age是需要我们自己设计的函数
			//如何设计看笔者之前的文章,这里就不再赘述
			printf("排序成功\n");
			break;
		case 2:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_name);
			printf("排序成功\n");
			break;
		case 3:
			qsort(c1->data, c1->sz, sizeof(peo), cmp_tel);
			printf("排序成功\n");
			break;
		case 0:
			return;
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);//无限循环除非choose为0
}
int main()
{
	int choose = 0;
	con c1;
	init_contact(&c1);
	do
	{
	    menu();
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
			add(&c1);//调用增加联系人的函数
			break;
		case 2:
			con_del(&c1);//调用删除联系人的函数
			break;
		case 3:
			dif(&c1);//调用修改联系人信息的函数
			break;
		case 4:
			search(&c1);//调用查找联系人的函数
			break;
		case 5:
			print_contact(&c1);//调用打印通讯录的函数
			break;
		case 6:
			sort(&c1);
			break;
		case 0:
			destroy_contact(&c1);//摧毁通讯录
			exit(1);//调用退出函数
			break;
		default:
			printf("语法错误,请重新输入\n");
			break;
		}
	} while (1);
	//写成无限循环,只能通过输入0退出循环
}

今天的分享到这里就结束了,感谢各位友友的来访,祝各位前程似锦O(∩_∩)O

猜你喜欢

转载自blog.csdn.net/fq157856469/article/details/131758518
今日推荐