C语言实现通讯录

一、前言

        通讯录是我们日常生活中不可或缺的一种功能,在手机联系人界面、QQ群、抖音都有着极其重要的作用,但是通讯录到底是怎么实现的呢?它的底层结构是怎样运行的?都是需要我们去学习和思考的,接下来我们一起用C语言来了解和学习通讯录的实现。

二、通讯录的结构

        如图中的一个QQ通讯录,我们可以清楚的看到上面的一个个联系人,这些联系人被分成不同的群组,每个群组中又包含着联系人的信息,那么这些数据是如何存储在其中的呢?

        在数据结构中,有顺序表、链表、树、图等结构,但是在通讯录的实现中要用到那些数据结构,则需要根据通讯录的功能及结构来确定。

        对于通讯录来说,我们在日常使用通讯录时,往往会随机的向通讯录中增、删、查、改一些数据,伴随着这些操作,通讯录中的数据也会随之改变,但是我们在使用通讯录的同时,并不会经常删除或者增加大量的联系人,而对联系人的查找和改动会多一些。

        如此一来,对比如上对通讯录的总结,那么顺序表结构就比较适合了,顺序表的特点为:能够随机访问数据、存储密度大、尾节点的插入删除方便但是对于中间节点的插入和删除较为繁琐,所以这些特点会比较符合通讯录的特征,由于考虑到通讯录会随着时间的推移而渐渐增加联系人,所以我们采用动态顺序表,以达到避免顺序表饱和的情况。

        所以本章将采用顺序表的结构实现通讯录。

三、底层代码、通讯录代码、主程序

        对于通讯录的实现,本文将采用VS来实现,同时对于底层代码,将采用命名为“sqlist.h”、“sqlist.c”的头文件和源文件,对于通讯录代码,将采用命名为“contact.h”、“contact.c”的头文件和源文件,主程序使用“program.c”的源文件。

3.1底层代码
3.1.1建立头文件和源文件

3.1.2声明

        为了写出来的代码具有一般性,我们一般事先声明所要用到的类型,代码如下:

#define N 4
typedef contact sq;
typedef struct SQLIST
{
	int size;
	int capacity;
	sq* arr;
}SQ;
3.1.3基本操作代码

        在顺序表的实现中,通常为顺序表的初始化、插入(头插、尾插、任意位置插入)、删除(头删、尾删、任意位置删除)、查找数据、改变数据、展示通讯录中的数据。

在头文件中定义如下:

//初始化
void SqlistInit(SQ* pf);
//尾插
void SqlistPushBck(SQ* pf,sq n);
//尾删
void SqlistDeleteBack(SQ* pf);
//查找数据
void SqlistFindData(SQ* pf, char* str);//以查找名字为例
//改变数据
void SqlistModifyData(SQ* pf, char* str);//以改变名字为例
//展示
void SqlistShow(SQ* pf);

在源文件中代码实现如下:

1、对于顺序表的初始化,我们首先应该创建空间(空间大小随意,这里默认4个空间大小)

//初始化
void SqlistInit(SQ* pf)
{
	(SQ*)pf->s = (SQ*)malloc(sizeof(SQ) * N);
	assert(pf);
	pf->capacity = N;//将顺序表的空间扩大到和申请空间大小的一致
	pf->size = 0;//让顺序表中数据的个数为0
}

2、我们要想往通讯录中插入联系人,必须将联系人的姓名、年龄、性别、电话号码一并传入通讯录中,所以这里要新建一个结构体,结构体中要包含上述联系人的信息,在我们一一输入完成后一并传入通讯录中。但是需要注意的是,我们在向顺序表

        我们要想往顺序表中插入数据时,首先要考虑的就是顺序表中的数据是否已经满了,如皋满了则要再次动态的扩大空间大小,如果不满直接插入即可,所以在此之前我们应该建立一个判断顺序表空间的函数,并且顺序表一旦为空就在函数里完成扩容。

//检查顺序表是否为满
void SqlistCheck(SQ* pf)
{
	assert(pf);
	if (pf->capacity == pf->size)//如果链表满了,就扩容。
	{
		SQ* tmp = (SQ*)realloc(pf->s, sizeof(SQ) * pf->capacity * 2);
		assert(tmp);//判断空间是否追加成功
		pf = tmp;
		pf->capacity *= 2;
	}
}

//尾插
void SqlistPushBck(SQ* pf, sq n)
{
	assert(pf);
	SqlistCheck(pf);判断链表是否为满
	pf->s[pf->size] = n;//将数据插入到顺序表尾部
	pf->size++;//顺序表的下标向后移动一位
}

3、对于顺序表的尾删除数据则是要简单许多,只需把pf->size减一即可,但是在删除之前要注意顺序表是否为空,如果为空就要终止删除操作。

//尾删
void SqlistDeleteBack(SQ* pf)
{
	assert(pf);
	assert(pf->size);//判断顺序表是否为空
	pf->size--;
}

4、查找数据,在此我们以查找联系人的姓名为例,因此我们要传入的不止是结构体的指针,还有联系人的姓名。

//查找数据
int SqlistFindData(SQ* pf, char* str)//以查找名字为例
{
	assert(pf);
	assert(str);
	if (pf->size == 0)
	{
		return -1;//如果顺序表为空,则返回-1,代表查无此人
	}
	for(int i=0;i<pf->size;i++)
	{
		if (strcmp(pf->s[i].name, str) == 0)
			return i;//找到了返回该数据在顺序表中的下标
	}
	return -1;//如果遍历整个顺序表没有找到,则返回-1,代表查无此人
}

5、要改变顺序表中数据的信息,首先要找到要改变的数据,再将传入的新数据替换该数据,但是如果顺序表为空或者该数据不存在,则报错。

//改变数据
void SqlistModifyData(SQ* pf, char* member, char* name)//以改变名字为例
{
	assert(pf);
	if (SqlistFindData(pf, member) == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
	strcpy(pf->s[SqlistFindData(pf, member)].name, name);
}

6、在完成输入数据后,我们想要知道顺序表中是否已经存储完成,这时我们就应该写一个打印函数,将顺序表中的数据打印出来。

//展示
void SqlistShow(SQ* pf)
{
	assert(pf);
	assert(pf->size);
	printf("姓名 年龄 性别 电话号码\n");
	for (int n = 0; n < pf->size; n++)
	{
		printf("%s  %d  %s  %s  ", pf->s[n].name, 
									 pf->s[n].age, 
									 pf->s[n].gander, 
									 pf->s[n].tel);
		printf("\n");                    
	}
}
3.2通讯录代码

        完成顺序表的底层代码后,我们就可以编写通讯录的程序了,这时我们只需做好前备工作,直接调用我们的底层代码即可。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
typedef struct SQLIST con;
#define NAME_MAX 20
#define TEL_MAX 20
#define GANDER_MAX 15
typedef struct CONTACT
{
	char name[NAME_MAX];
	int age;
	char gander[GANDER_MAX];
	char tel[TEL_MAX];
}contact;

//初始化通讯录
void ContactInit(con* pf);
//插入联系人
void ContactPushBack(con* pf);
//删除通讯录中的联系人
void ContactDelete(con* pf);
//修改通讯录中联系人的信息,以名字为例
void ContactModify(con* pf);
//查找通讯录中联系人的信息
void ContactCheck(con* pf);
//展示通讯录
void ContactShow(con* pf);
//初始化通讯录
void ContactInit(con* pf)
{
	SqlistInit(pf);
}


//插入联系人
void ContactPushBack(con* pf)
{
	assert(pf);
	contact cc;
	printf("请输入要插入联系人的姓名:");
	scanf("%s", cc.name);
	printf("请输入要插入联系人的年龄:");
	scanf("%d", &(cc.age));
	printf("请输入要插入联系人的性别:");
	scanf("%s", cc.gander);
	printf("请输入要插入联系人的电话号码:");
	scanf("%s", cc.tel);
	SqlistPushBck(pf, cc);
}

//删除通讯录中的联系人
void ContactDelete(con* pf)
{
	SqlistDeleteBack(pf);
}

//查找通讯录中联系人的信息
void ContactCheck(con* pf)
{
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找的联系人姓名:");
	scanf("%s", name);
	if (SqlistFindData(pf, name) == -1)
		printf("查无此人\n");
	else
		printf("找到了,此人在通讯录中的下标为:%d", SqlistFindData(pf, name));
}

//展示通讯录
void ContactShow(con* pf)
{
	SqlistShow(pf);
}

//改变数据
void ContactModify(con* pf)
{
	char member[20] = { 0 };
	char name[20] = { 0 };
	printf("请输入要改变联系人的姓名:");
	scanf("%s", member);
	printf("请输入新的姓名:");
	scanf("%s", name);
	SqlistModifyData(pf, member, name);
}
3.3主程序

        写完底层代码和通讯录代码后,我们就可以进行测试了,在此我们首先输入三个人的信息即:11 11 11 11,22 22 22 22,33 33 33 33。输入完成进行展示通讯录,查看是否输入成功,之后再删除33的数据,最后再把11的姓名改成33,展示通讯录。

代码如下:

void func1()
{
	SQ s;
	ContactInit(&s);//初始化
	ContactPushBack(&s);//插入
	ContactPushBack(&s);//插入
	ContactPushBack(&s);//插入
	ContactShow(&s);//展示
	ContactDelete(&s);//删除 
	ContactShow(&s);//展示
	ContactModify(&s);//修改数据
	ContactShow(&s);//展示
}
int main()
{
	func1();
	return 0;
}

四、总结

        以上便是通讯录的大体步骤,其中最核心的代码也是顺序表的核心操作,即增删改查,所以只要我们熟悉顺序表的操作,那么对于类似于通讯录问题的实现也就变得简单了,只需做好前备工作直接调用底层代码(顺序表的基本操作)即可。

                                                                                                              

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值