简易结构体实现通讯录

我们都知道通讯录是存着许多个人的信息,描述不同的数据c语言有不同的类型,但是如果要描述一个人,他的年龄,地址,姓名,电话,又该用什么类型描述呢,这个时候C语言提供一种自定义类型——结构体,我们可以用这个结构体定义一种变量,来存储描述一个人的信息。

typedef struct p_infor
{
	char name[name_max];    存放姓名
	char sex[sex_max];      存放性别
	int  age;               存放年龄
	char number[num_max];   存放电话
	char poisition[poi_max];存放地址
}p_infor;

当要存储多个同种类型的元素时则用数组,也就是特殊的结构体数组(但是数组空间大小固定,不够灵活,我们等会可以优化)。我们这个通讯录拥有如下功能增 删 查 改,打 印 显 示,在这些功能中,我们都必须先知道结构体数组中的元素个数,因为容量是有限的,要根据元素个数判断是否能继续增加(如果定义为全局变量,不方便其它文件调用,并且若是出现过多的全局变量,我认为统一维护也是麻烦的事),我们可以把结构体数组和该数组中的元素个数再合并为一个结构体。这个结构体称为通讯录结构体contact

typedef struct pc
{
	int sz;
	p_infor data[max];//通讯录结构体,包含信息和存放信息个数
}contact;类型重新定义为contact

功能1.初始化:

结构体实质是一种类型,我们还要用这个类型定义通讯录变量,才会为存储信息开辟空间,我们在使用变量时都要初始化,结构体类型的初始化更加复杂,我们封装在一个函数内实现

void initcontact(contact* con)
{
	assert(con);//检查指针是否有效
	con->sz = 0;//元素个数归为零
	memset(con->data, 0, sizeof(p_infor)*max);//数组内初始化为0 
}memst参数1初始化的地址   0是要初始化的结果,第三个参数是初始化空间的大小

功能2.增加信息:

void Addcontact(contact*con )
{
	assert(con);//检查指针有效性

	if (con->sz == max)//如果数组已满,则停止添加
	{
		printf("通讯录已经满了\n");
		return;
	}
	printf("请输入姓名");
	scanf("%s", con->data[con->sz].name);
	printf("请输入性别");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入年龄");
	scanf("%d", &(con->data[con->sz].age));变量赋值要取地址,其它的都是字符数组赋值,
                                           只需要数组名即可 
                                           
	printf("请输入电话号码");
	scanf("%s", con->data[con->sz].number);
	printf("请输入地址");
	scanf("%s", con->data[con->sz].poisition);
	(con->sz)++;//添加完成,sz++,表示数组内存放元素+1
	printf("添加成功\n");//打印提示信息
	return;
}

功能3.打印通讯录:

(该功能提前实现可以方便我们检验增加和删减等功能的效果)

void Printfcontact(contact* con)
{
	assert(con);
	int i = 0;
	printf("%-20s %-5s %-10s %-12s %-20s\n","姓名","性别","年龄","电话","地址");
//打印信息表头
	for (i = 0; i < con->sz; i++)//循环打印人的信息
	{    打印对齐处理
		printf("%-20s %-5s %-10d %-12s %-20s",con->data[i].name,
			con->data[i].sex,
			con->data[i].age,
			con->data[i].number,
			con->data[i].poisition);
		printf("\n");
	}
}

 辅助函数:通过姓名查找该人是否存在

(由于删除人的信息和查找人的信息目前设计都是用姓名查找,所以我们将姓名查找封装为一个函数,方便使用)

int findbyname(contact *con,char*name)
{
	assert(con&&name);
	int i = 0;
	for (i = 0; i < con->sz; i++)//循环遍历比较
	{
		if (0 == strcmp(con->data[i].name, name))//传地址
			return i;
	}
	return -1;//找不到名字,返回-1
}

功能4.删除信息:

void deltcontact(contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		return;//如果通讯录无信息,无法删除
	}
	//输入要删除人的姓名
	char name[name_max] = { 0 };
	printf("请输入要删除人的姓名");
	scanf("%s", name);
	//查找要删除人的姓名,后面要实现修改,查找信息都要用到查找功能,
//所以我们在此处把按姓名查找封装为一个函数,便于复用。

	int poi=findbyname(con, name);//找到返回相对偏移量
	if (-1 == poi)
	{
		printf("找不到要删除的人\n");打印提示
		return;
	}
    //删除
	int i = 0;
	for (i = poi; i < con->sz-1; i++)
	{
		con->data[i] = con->data[i + 1];最后一个元素也可以删除
	}
	con->sz--;
	printf("删除成功\n");
}

功能5.根据姓名查找个人信息并显示:

void searchcontact( contact* con)
{
	assert(con);
	char name[name_max] = { 0 };
	printf("请输入要查找人信息的姓名");
	scanf("%s", name);
	int poi = findbyname(con, name);
	if (-1 == poi)
	{
		printf("找不到该名字\n");
		return;            //未找到,提前返回  
	}
   //找到了,打印显示
	printf("%-20s %-5s %-10s %-12s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%-20s %-5s %-10d %-12s %-20s", con->data[poi].name,
		con->data[poi].sex,
		con->data[poi].age,
		con->data[poi].number,
		con->data[poi].poisition);
	printf("\n");
}

功能6.修改个人信息:

void modifycontact(contact* con)
{
	assert(con);
	char name[name_max] = { 0 };
	printf("请输入要修改者的姓名\n");
	scanf("%s", name);
	int poi = findbyname(con, name);//要修改一个人的信息还是得先找到它,这就是封装为函数的
	if (-1 == poi)
	{
		printf("找不到该名字");
		return;      函数不需返回值也可以用return结束函数
	}
如果找到了就不会提前返回而是提示输入信息
	printf("请输入修改的姓名");
	scanf("%s", con->data[poi].name);
	printf("请输入修改的性别");
	scanf("%s", con->data[poi].sex);
	printf("请输入修改的年龄");
	scanf("%d", &(con->data[poi].age));
	printf("请输入修改的电话号码");
	scanf("%s", con->data[poi].number);
	printf("请输入修改的地址");
	scanf("%s", con->data[poi].poisition);
	printf("修改成功\n");
}

功能7.排序通讯录:

我们微信的联系人按名字首字母来排列,我们的通讯录中如果存放了太多人的信息也可以用排序将其排列整齐。(在我其它博客中有qsort的详细模拟,大家可看那篇博客加深理解)

int cmp_age(const void* p1, const void* p2)
{
	
	return ((p_infor*)p1)->age-((p_infor*)p2)->age;
}
int cmp_name(const void* p1,const void* p2)
{

	return strcmp(((p_infor*)p1)->name ,((p_infor*)p2)->name);
}
void qsortcontact(contact* con)
{
	
	printf("0.退出排序\n");选择排序方法
	printf("1.按年龄排\n");
	printf("2.按姓名排\n");
	int a = 0;
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			qsort(con->data,con->sz, sizeof(con->data[0]),cmp_age);
			printf("排序成功\n");
			break;
		case 2:
			qsort(con->data, con->sz, sizeof(con->data[0]), cmp_name);
//qsort参数自左向右分别为排序的,元素个数,元素大小,以及回调函数cmp_name,

			printf("排序成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
}

功能8.清空通讯录:

清空通讯录我调用的是初始化函数。(偷个懒)

一个简易的通讯录就做好了,但是我们在做通讯录并没有对同名的人进行处理,小何在此再偷个懒,大家可以试试。目前这个通讯录有两个弊端,一:空间分配不灵活 二:保存的信息是在内存上的,这使得程序结束后信息就丢失了。

弊端二解决方法:这就要涉及到c语言中的文件了。

我们只要退出的时候调用savecontact这个函数把信息保存到文件就不会丢失了,但是下一次打开程序我们要从文件中读取信息到内存中。

void savecontact(contact* con)
{
	//打开文件,用二进制书写的方式打开,当没有这个contact.txt这个文件时,
    //读写方式打开会帮我们新建立一个文件
	FILE* p = fopen("contact.txt", "wb");
     //成功打开后会把文件指针返回来,
     //我们就用这个指针读取文件的数据
	if (NULL == p)
	{
		perror("savecontact");//如果为空,perror函数会将错误码转为错误信息并打印出来
                            //括号中的"savecontact"不加也可以,只是方便我们知道出错的位置
	}
	else
	{
		FILE*pf = p;
		int i = 0;
		for (i = 0; i < con->sz; i++)
		{
			fwrite(con->data + i, sizeof(p_infor), 1, pf);
		}
从con->data+i指向的空间读取一个p_infor大小的数据到文件指针pf指向的文件

		printf("保存成功\n");
		fclose(pf);关闭文件
		pf = NULL;
		p = NULL;
	}
}

弊端一解决方法:c语言提供了库函数malloc和realloc可以灵活地开辟空间,不够我们再加,多了我们就减少,非常灵活。首先要改变的就是结构体数组的存储方法,由于我们需要判断动态空间的容量,所以在此增加一个变量记录容量

typedef struct pc
{
	int capicity;//data的容量
	int sz;//数组存放人信息个数
	p_infor* data;//data指向存放人信息的动态空间
}contact;

我们现在是把data从指向数组改成指向一个动态开辟的空间

void initcontact(contact* con)
{
	assert(con);
	con->sz = 0;
	                    //calloc开辟一段内存储存人的信息
	p_infor*ptr = (p_infor*)calloc(init_cap,sizeof(p_infor));
	if (ptr == NULL)      //calloc开辟init_cap个p_infor大小的空间
	{
		perror("initcontact");
		return;  若开辟失败提前返回,并用perror打印错误信息
	}
	con->data=ptr;
若开辟成功才可将指针赋值,否则失败时会把空指针用来赋值
	
   con->capicity = init_cap;
   //载入信息到通讯录中
	
   loadcontact(con); 
由于我们在之后有个将通讯录保存到文件中的操作,所以当我们再次运行程序的时候
要把信息从文件加载到内存中
}

加载信息:

保存到文件只是程序结束不会丢失,但是想下一次程序运行能在内存看到还得加载到内存中。

void loadcontact( contact * con)
{
	//打开文件
	FILE* p = fopen("contact.txt","rb");将文件以二进制读取的方式打开
	if (NULL == p)
	{
		perror("savecontact");检查是否为空指针
	}
	else
	{
		FILE* pf = p;
		p = NULL;   p在赋值后要置为空,
		//读入数据
		int i = 0;
		p_infor tmp = { 0 };//结构体类型初始化
		while (fread(&tmp, sizeof(p_infor), 1, pf))
		{
       fread每次从pf指向的文件读取一个p_infor大小的数据到tmp中
      应该先检查空间是否足够再存到内存中,所以用tmp临时保存,而不是直接存储

			check_contact(con);
        检查动态开辟空间的容量
			con->data[i] = tmp;
        虽然是动态开辟的空间,但是使用上和数组类似,因为data[i]等价与*(data+i) 
			i++;
			con->sz++;
         存放元素+1
		}
		printf("载入信息成功\n");
		fclose(pf);关闭文件
		pf = NULL;
	}
}

辅助函数check_contact,

这个是检查malloc开辟的空间是否足够存放,如果不够我们就要用realloc增加空间

void check_contact(contact* con)
{
	if (con->sz == con->capicity)
	{
		//增容
		p_infor* ptr = (p_infor*)realloc(con->data, sizeof(p_infor) * (con->capicity + increase));              
		if (ptr == NULL)
		{
			perror("Addcontact");
			return;
		}
		con->data = ptr;//指向新空间地址
		con->capicity += increase;//成功后容量增加
		printf("增容成功\n");
	}
}

感谢支持,希望对大家有所帮助

完整代码

​​​​​​​test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
	printf("            请选择              \n");
	printf("*******1.add     2.delt    *****\n");
	printf("*******3.search  4.modify  *****\n");
	printf("*******5.Printf  6.sort    *****\n");
	printf("*******7.clean   0.exit    *****\n");
}
enum poisition
{
	add = 1,
	delt,
	search,
	modify,
	Print,
	sort,
	clean
};
int main()
{
	int a = 0;//记录功能选择
	contact con;//定义一个通讯录变量
	//初始化结构体数组
	initcontact(&con);
	do {
		menu();
		scanf("%d", &a);
		switch (a)
		{
		case add:
			Addcontact(&con);//
			break;
		case delt:
			deltcontact(&con);//
			break;
		case search:
			searchcontact(&con);//
			break;
		case modify:
			modifycontact(&con);//
			break;
		case Print:
			Printfcontact(&con);//
			break;
		case sort:
			qsortcontact(&con);
			break;
		case clean:
			//clearcontact(&con);静态清空通讯录
			destroycontact(&con);//动态清空通讯录
			break;
		case 0:
			savecontact(&con);//文件保存信息
			destroycontact(&con);//退出清空动态开辟的通讯录
			printf("退出通讯录");
			break;
		default:
			printf("请重新输入");
			break;
		}
	} while (a);
	return 0;
}

contact.h

#include
#include
#include
#include
#define name_max 20
#define sex_max  5
#define num_max  12
#define poi_max  100
#define init_cap  3
#define increase 2
//#define max  100

typedef struct p_infor
{
	char name[name_max];//类型定义放在此处,方便包含头文件
	char sex[sex_max];
	int  age;
	char number[num_max];
	char poisition[poi_max];
}p_infor;
typedef struct pc
{
	int capicity;//data的容量
	int sz;//数组存放人信息个数
	p_infor* data;//data指向存放人信息的动态空间
}contact;
//静态版本
//typedef struct pc
//{
//	int sz;
//	p_infordata[max];//通讯录结构体,包含信息和存放位置
//}contact;

// 动态清空通讯录
void destroycontact(contact* con);
//初始化通讯录
void initcontact(contact* con);
//添加通讯录
void Addcontact(contact* con);
//打印通讯录
void Printfcontact(contact* con);
//删除通讯录里的信息
void deltcontact(contact* con);
//从通讯录查找信息
void searchcontact(contact* con);
//修改通讯录信息
void modifycontact(contact* con);
//排序通讯录
void qsortcontact(contact* con);
//清空通讯录联系人
void clearcontact(contact* con);
//保存信息到文件
void savecontact(contact*con);

contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"
int findbyname(contact *con,char*name)
{
	assert(con&&name);
	int i = 0;
	for (i = 0; i < con->sz; i++)
	{
		if (0 == strcmp(con->data[i].name, name))//传地址
			return i;
	}
	return -1;//找不到名字,返回-1
}
void check_contact(contact* con)
{
	if (con->sz == con->capicity)
	{
		//增容
		p_infor* ptr = (p_infor*)realloc(con->data, sizeof(p_infor) * (con->capicity + increase));
		if (ptr == NULL)
		{
			perror("Addcontact");
			return;
		}
		con->data = ptr;//指向新空间地址
		con->capicity += increase;//成功后容量增加
		printf("增容成功\n");
	}
}
void loadcontact( contact * con)
{
	//打开文件
	FILE* p = fopen("contact.txt","rb");
	if (NULL == p)
	{
		perror("savecontact");
	}
	else
	{
		FILE* pf = p;
		p = NULL;
		//读入数据
		int i = 0;
		p_infor tmp = { 0 };//结构体类型初始化
		while (fread(&tmp, sizeof(p_infor), 1, pf))
		{
			check_contact(con);
			con->data[i] = tmp;
			i++;
			con->sz++;
		}
		printf("载入信息成功\n");
		fclose(pf);
		pf = NULL;
	}
}
//动态版本
void initcontact(contact* con)
{
	assert(con);
	con->sz = 0;
	//calloc开辟一段内存储存人的信息
	p_infor*ptr = (p_infor*)calloc(init_cap,sizeof(p_infor));
	if (ptr == NULL)
	{
		perror("initcontact");
		return;
	}
	con->data=ptr;
	con->capicity = init_cap;
	//载入信息到通讯录中
	loadcontact(con);
}
//静态版本
//void initcontact(contact* con)
//{
//	assert(con);
//	con->sz = 0;
//	memset(con->data, 0, sizeof(p_infor)*max); 
//}
// 静态版本
//void Addcontact(contact*con )
//{
//	assert(con);
//
//	if (con->sz == max)
//	{
//		printf("通讯录已经满了\n");
//		return;
//	}
//	printf("请输入姓名");
//	scanf("%s", con->data[con->sz].name);
//	printf("请输入性别");
//	scanf("%s", con->data[con->sz].sex);
//	printf("请输入年龄");
//	scanf("%d", &(con->data[con->sz].age));
//	printf("请输入电话号码");
//	scanf("%s", con->data[con->sz].number);
//	printf("请输入地址");
//	scanf("%s", con->data[con->sz].poisition);
//	(con->sz)++;
//	printf("添加成功\n");
//	return;
//}

void Addcontact(contact* con)
{
	assert(con);
	check_contact(con);
	printf("请输入姓名");
	scanf("%s", con->data[con->sz].name);
	printf("请输入性别");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入年龄");
	scanf("%d", &(con->data[con->sz].age));
	printf("请输入电话号码");
	scanf("%s", con->data[con->sz].number);
	printf("请输入地址");
	scanf("%s", con->data[con->sz].poisition);
	(con->sz)++;
	printf("添加成功\n");
	return;
}
void Printfcontact(contact* con)
{
	assert(con);
	int i = 0;
	printf("%-20s %-5s %-10s %-12s %-20s\n","姓名","性别","年龄","电话","地址");
	for (i = 0; i < con->sz; i++)
	{
		printf("%-20s %-5s %-10d %-12s %-20s",con->data[i].name,
			con->data[i].sex,
			con->data[i].age,
			con->data[i].number,
			con->data[i].poisition);
		printf("\n");
	}
}
void deltcontact(contact* con)
{
	assert(con);
	if (con->sz == 0)
	{
		return;//如果通讯录无信息,无法删除
	}
	//输入要删除人的姓名
	char name[name_max] = { 0 };
	printf("请输入要删除人的姓名");
	scanf("%s", name);
	//查找要删除人的姓名
	int poi=findbyname(con, name);//传通讯录地址
	if (-1 == poi)
	{
		printf("找不到要删除的人\n");
		return;
	}
    //删除
	int i = 0;
	for (i = poi; i < con->sz-1; i++)
	{
		con->data[i] = con->data[i + 1];//直接按元素删除即可,最后一个元素也可以删除
	}
	con->sz--;
	printf("删除成功\n");
}
void searchcontact( contact* con)
{
	assert(con);
	char name[name_max] = { 0 };
	printf("请输入要查找人信息的姓名");
	scanf("%s", name);
	int poi = findbyname(con, name);
	if (-1 == poi)
	{
		printf("找不到该名字\n");
		return;
	}
	printf("%-20s %-5s %-10s %-12s %-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	printf("%-20s %-5s %-10d %-12s %-20s", con->data[poi].name,
		con->data[poi].sex,
		con->data[poi].age,
		con->data[poi].number,
		con->data[poi].poisition);
	printf("\n");
}
void modifycontact(contact* con)
{
	assert(con);
	char name[name_max] = { 0 };
	printf("请输入要修改者的姓名\n");
	scanf("%s", name);
	int poi = findbyname(con, name);
	if (-1 == poi)
	{
		printf("找不到该名字");
		return;
	}
	printf("请输入修改的姓名");
	scanf("%s", con->data[poi].name);
	printf("请输入修改的性别");
	scanf("%s", con->data[poi].sex);
	printf("请输入修改的年龄");
	scanf("%d", &(con->data[poi].age));
	printf("请输入修改的电话号码");
	scanf("%s", con->data[poi].number);
	printf("请输入修改的地址");
	scanf("%s", con->data[poi].poisition);
	printf("修改成功\n");
}
int cmp_age(const void* p1, const void* p2)
{
	
	return ((p_infor*)p1)->age-((p_infor*)p2)->age;
}
int cmp_name(const void* p1,const void* p2)
{

	return strcmp(((p_infor*)p1)->name ,((p_infor*)p2)->name);
}
void qsortcontact(contact* con)
{
	
	printf("0.退出排序\n");
	printf("1.按年龄排\n");
	printf("2.按姓名排\n");
	int a = 0;
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			qsort(con->data,con->sz, sizeof(con->data[0]),cmp_age);
			printf("排序成功\n");
			break;
		case 2:
			qsort(con->data, con->sz, sizeof(con->data[0]), cmp_name);
			printf("排序成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
}
void destroycontact(contact* con)
{
	free(con->data);
	con->data = NULL;
	con->capicity = 0;
	con->sz = 0;
}
void clearcontact(contact* con)
{
	initcontact(con);
}
void savecontact(contact* con)
{
	//打开文件
	FILE* p = fopen("contact.txt", "wb");
	if (NULL == p)
	{
		perror("savecontact");
	}
	else
	{
		FILE*pf = p;
		int i = 0;
		for (i = 0; i < con->sz; i++)
		{
			fwrite(con->data + i, sizeof(p_infor), 1, pf);
		}
		printf("保存成功\n");
		fclose(pf);
		pf = NULL;
		p = NULL;
	}
}

你可能感兴趣的:(c语言)