【C】通讯录详解及二级优化

CSDN话题挑战赛第2期
参赛话题:学习笔记

前言

这篇文章对如何写出一个通讯录进行了详细的讲解,希望能对刚开始学习、想要写一个通讯录的同学提供一些帮助。同时博主还利用枚举动态内存对通讯录进行了一定的优化,能让你的通讯录更加的强大。

一.旧版大致思路

  • 我们都用过手机上的通讯录,手机的通讯录支持我们存储联系人的姓名、电话、住址等信息,而且通讯录中有不同的人,每个人对应不同的个人信息,如何才能保存一个人的各种信息呢?

  • 这时我们想到可以利用结构体,因为结构体中可以保存不同的信息,年龄可以保存在其中的int类型变量中,名字和地址可以保存在里面的字符串变量中。同时通讯录中肯定不只含有一个用户,这时我们需要用另一个通讯录来包含上面的通讯录数组变量(后面详解),这样就把存储空间搞定了

  • 通讯录要实现很多功能,本文的通讯录能够实现增删查改以及排序的功能,怎样实现这些功能呢?这时就需要我们通过各种函数进行逐一实现了

  • 我们希望这个通讯录能根据我们的需求实现不同的功能,如何实现呢?我们能够联想到switch函数,他不是正好可以根据我们不同的输入来进行不同的操作么?如此看来理论上貌似是可行的,让我们开始实际的操作吧。

二.旧版通讯录

1.框架构建

1.1存储空间

在思路中给我们有提到用结构体来储存一个人的各种信息,这里我们设计了一个peoinfo结构体来储存名字、性别、电话、年龄以及地址

struct peoinfo
{
	char name[10];
	char sex[5];
	char tele[20];
	int age;
	char addr[20];
};//别忘了分号

通讯录里当然不只含有一名用户,我们需要定义一个struct peoinfo类型的数组并且把他包含到另一个结构体contact中,contact里面还需要变量sz来记录通讯录里面有多少个用户。

struct contact
{
	struct peoinfo data[100];//此时的通讯录只能存储100个用户
	int sz;//通讯录中的人数
};

这样我们就能把所有用户存储到通讯录中了。

1.2菜单

通讯录能够按照我们的输入来进行相应的功能,不过对于使用的用户,我们希望通讯录能够在使用者开始使用时弹出一个菜单,菜单用来介绍通讯录的功能以及对应的命令。

void menu()
{
	printf("*********************************\n");
	printf("      1.add       2.delete       \n");
	printf("      3.search    4.show         \n");
	printf("      5.change    6.sort         \n");
	printf("      0.exit                     \n");//exit表示退出通讯录
	printf("*********************************\n");
}

运行后是这样的效果
在这里插入图片描述

1.3功能实现框架

存储空间有了,菜单有了,现在我们要想办法让我们的命令和我们写的函数连接起来,switch语句能实现这个功能。do-while语句能实现通讯录的连续使用

int main()
{
	int input = 0;
	struct contact con;//struct contact类型的变量
	init_con(&con);//因为我们需要修改通讯录的内容因此需要传址,下面的实参同理
	do
	{
		printf("你的选择是?\n");
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1://数字对应菜单中的数字
			add_con(&con);//传址才能改变con的内容
			break;
		case 2:
			del_con(&con);
			break;
		case 3:
			search_con(&con);
			break;
		case 4:
			show_con(&con);
			break;
		case 5:
			change_con(&con);
			break;
		case 6:
			sort_con(&con);
			break;
		case 0:
			break;
		default:
			printf("请重新输入\n");
		}
	} while (input);//分号别忘了
	return 0;
}

这样程序就能根据我们不同的输入来进行不同的操作了。

2.实现功能函数

2.0初始化

void init_con(struct contact* con)
{
	memset(con->data, 0, 100 * sizeof(con->data[0]));
	con->sz = 0;
}

在这里插入图片描述机翻,想进一步了解的读者可以在CSDN查一下。

通过memset我们就可以初始化通讯录了。

2.1添加用户

void add_con(struct contact* con)	
{
	printf("姓名\n");	
	scanf("%s", con->data[con->sz].name);
	printf("性别\n");
	scanf("%s", con->data[con->sz].sex);
	printf("电话\n");
	scanf("%s", con->data[con->sz].tele);
	printf("年龄\n");
	scanf("%d", &(con->data[con->sz].age));
	printf("地址\n");
	scanf("%s", con->data[con->sz].addr);
	con->sz++;
}

记得每次添加完使sz++,记录有多少用户能方便我们后面函数的书写。

2.2删除用户

在写删除函数的时候我们需要思考一个问题,删除一个用户的前提是找到一个用户,而且找到用户的功能在删除、查找、修改这三个函数中都需要使用,因此我们先书写一个简单的查找函数(通过名字查找)。

static int find(char* name, struct contact* con)
{
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

当函数返回值是 i(数组中第i+1位用户),证明存在这个用户,反之不存在。这样我们就能通过返回值找到这个用户在第几位。

strcmp用来比较两字符串的大小。

现在我们可以写删除函数了

void del_con(struct contact* con)
{
	char* name[10];
	printf("你想要删除的名字是\n");
	scanf("%s", name);
	int i = find(name, con);
	if (i != -1)
	{
		for (; i <= con->sz - 1; i++)
		{
			con->data[i] = con->data[i + 1];
		}
		printf("删除成功\n");
		con->sz--;
	}
	else
	{
		printf("用户不存在\n");
	}
}

这里我们采用被删除用户后一位替换被删除位并以此类推做法,也可以使用memcpy。

2.3查找用户

既然我们已经写了一个查找的函数,那么这一步就变得很简单了,我们只需要完善这个函数的功能即查找后输出查找用户的所有信息就行了。

void search_con(struct contact* con)
{
	char* name[10];
	printf("你想要查找的名字是\n");
	scanf("%s", name);
	int i = find(name, con);
	if (i != -1)
	{
		printf("%-10s\t", "姓名");//负号能实现左对齐
		printf("%-5s\t", "性别");//数字加上\t可以实现数据的对齐
		printf("%-20s\t", "电话");
		printf("%-10s\t", "年龄");
		printf("%-20s\t", "地址");
		printf("\n");
		printf("%-10s\t", con->data[i].name);
		printf("%-5s\t", con->data[i].sex);
		printf("%-20s\t", con->data[i].tele);
		printf("%-10d\t", con->data[i].age);
		printf("%-20s\t", con->data[i].addr);
		printf("\n");
	}
}

2.4展示所有用户

如果我们想要看到目前通讯录中的所有用户呢?

void show_con(struct contact* con)
{
	int i = 0;
	printf("%-10s\t", "姓名");
	printf("%-5s\t", "性别");
	printf("%-20s\t", "电话");
	printf("%-10s\t", "年龄");
	printf("%-20s\t", "地址");
	printf("\n");
	for (i = 0; i < con->sz; i++)
	{
		printf("%-10s\t", con->data[i].name);
		printf("%-5s\t", con->data[i].sex);
		printf("%-20s\t", con->data[i].tele);
		printf("%-10d\t", con->data[i].age);
		printf("%-20s\t", con->data[i].addr);
		printf("\n");
	}
}
  • 负号能实现左对齐

  • \t和数字能够实现同样的数据对齐

看一下大概效果:
在这里插入图片描述

2.5修改用户信息

此函数同样需要查找函数

void change_con(struct contact* con)
{
	char* name[10];
	printf("你要修改的用户是\n");
	scanf("%s", name);
	int i = find(name, con);
	printf("姓名\n");
	scanf("%s", con->data[i].name);
	printf("性别\n");
	scanf("%s", con->data[i].sex);
	printf("电话\n");
	scanf("%s", con->data[i].tele);
	printf("年龄\n");
	scanf("%d", &(con->data[i].age));
	printf("地址\n");
	scanf("%s", con->data[i].addr);
}

需要注意的是,此函数只能实现将要修改的用户信息重新输入,无法精准修改某一项信息,如果想实现可以再设计一个菜单。

2.6用户信息排序

这里我们采用的是用名字排序

int my_cmp(void* e1, void* e2)
{
	return (strcmp(((struct peoinfo*)e1)->name, ((struct peoinfo*)e2)->name));
}

void sort_con(struct contact* con)
{
	qsort(con->data, con->sz, sizeof(con->data[0]), my_cmp);
	show_con(con);
}

在这里插入图片描述
qsort实现快速排序,是一个非常好用的库函数。

三.一级优化

我们采用枚举来对结构体进行优化,不知道各位在写功能框架(switch语句那里)时会不会感觉有些麻烦在这里插入图片描述因为写到case某个数字的时候我们都需要翻到我们的菜单,查看对应的功能是什么,需要写那个函数。

枚举可以解决这个问题,我们先定义一个枚举

enum num
{
	Exit,
	add,
	Delete,
	search,
	show,
	change,
	sort
};

我们知道在枚举中,成员其实是一个个的常量,且在没有赋值的情况下,第一个成员默认赋值为0,后面以此类推,这样我们就可以用枚举成员替换case后面的数字。

do
	{
		printf("你的选择是?\n");
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case add:
			add_con(&con);
			break;
		case Delete:
			del_con(&con);
			break;
		case search:
			search_con(&con);
			break;
		case show:
			show_con(&con);
			break;
		case change:
			change_con(&con);
			break;
		case sort:
			sort_con(&con);
			break;
		case Exit:
			break;
		default:
			printf("请重新输入\n");
		}
	} while (input);

一级优化算是对程序的书写提供了一定的便利

四.二级优化

1.意义

在旧版通讯录中我们不难发现,因为我们定义了

struct peoinfo data[100];

因此我们的通讯录只能存储100个用户的信息,如果我们需要存储1000个用户此时这些空间就不够用了,如果我们只需要存储两个用户,那么剩下的98个空间就被浪费了,有没有什么方法能解决这个问题呢?这时就需要我们的动态内存分配了。

使用动态内存分配我们能够实现用多少开辟多少空间,提高我们通讯录的性能

2.存储

先回顾一下之前的存储方法
在这里插入图片描述使用动态内存后,我们希望新的空间一开始能存储三个用户的信息,当存储满了就会自动生成新的空间
在这里插入图片描述

3.代码实现

3.1存储

struct peoinfo
{
	char name[10];
	char sex[5];
	char tele[20];
	int age;
	char addr[20];
};

struct contact
{
	struct peoinfo* data;
	int sz;//通讯录中的人数
	int check;//达到三个就重置
};

3.2初始化

动态内存的初始化跟旧版的不太一样,而且我们需要先开辟初始的三个空间

void init_con(struct contact* con)
{
	con->sz = 0;
	con->check = 0;
	con->data = (struct peoinfo*)calloc(3, sizeof(struct peoinfo));
}

3.3判断

动态通讯录的精髓在于能够自动开辟空间,每次空间满了,check都会达到3,这时相应函数就会再开辟三个空间同时给check重新赋值为0。我们需要在add函数中添加这个判断。。

void add_con(struct contact* con)
{
	printf("姓名\n");
	scanf("%s", con->data[con->sz].name);
	printf("性别\n");
	scanf("%s", con->data[con->sz].sex);
	printf("电话\n");
	scanf("%s", con->data[con->sz].tele);
	printf("年龄\n");
	scanf("%d", &(con->data[con->sz].age));
	printf("地址\n");
	scanf("%s", con->data[con->sz].addr);
	con->sz++;
	con->check++;
	if (con->check == 3)
	{
		con->data = (struct peoinfo*)realloc(con->data, (con->sz + 3) * sizeof(struct peoinfo));
		con->check = 0;
	}
}

3.4释放

最后一定要记得释放,否则可能导致内存泄漏。

	free(con.data);

因为我们其实只是将数组换成了指针,因此不影响其它函数的使用。

五.最后

这篇文章博主进行了通讯录的二级优化,但这个通讯录依然有很多优化的空间,比如运行结束存储的数据就会消失,这就需要运用文件管理的知识,因此后面可能会进一步优化,敬请期待

博主码文不易,如果觉得这篇文章对你有帮助的话,可以点赞收藏关注。你的支持是我创作的最大动力!

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诺伯里-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值