通讯录的实现(静态版本和动态版本和文件版本)

为什们要实现通讯录?

主要是为了让我们将结构体的知识,了解的更加深刻,将结构体应用一下,我们先将静态的通讯录实现,在进行改良,用动态内存的知识再将通讯录改造一边,将动态内容的知识也运用一下,最后再用文件操作的方式在改造一下通讯录。
所以我们会写三个通讯录,同时他是循序渐进的,一步一步的,如果不动前面的知识,可以去看看前面的知识点。

  1. 静态版本通讯录
  2. 动态版本通讯录
  3. 文件版本通讯录

🐖静态通讯录

主要运用结构体的知识,不会的同学可以看看《自定义类型保姆级教学(结构体,位段,枚举,联合)》

首先先介绍一下代码的主题框架有三个部分test.c ,contact.c,contact.h
test.c的作用:测试通讯录的实现,也就是主函数,整个代码的操作逻辑
🐖contact.c的作用:写通讯录的实现(通讯录代码函数实现)
🐖contact.h的作用:写通讯录的声明(通讯录代码函数声明)

在引用头文件时需要注意的地方
🐖 contact.h中放通讯录的声明,在 test.ccontact.c的文件都要应用该声明,所以为什们我们不把 #include<stdio.h>这些头文件也放到声明里,我们就不需要在 test.ccontact.c的文件重复定义了,那么怎么在在 test.ccontact.c的文件中引用 contact.h文件呢,
🐕我们知道引用外部文件时,用双引号所以可以用 include"contact.h",这样在 contact.h中存放的声明都可以使用。

对于通讯录需要存放100个人的信息,每个人的信息包括(姓名,性别,电话,年龄,住址)
而且通讯录还要有以下功能,
🐶 (1)增加联系人
🐶 (2)删除指定联系人
🐶 (3)修改指定联系人
🐶 (4)查找指定联系人
🐶 (5)排序
🐶 (6)显示通讯录的信息

🍔通讯录的代码实现

🦊(1)通讯录的外部封装

对于通讯录不可能看完做完一个操作就不做了,所以应该写一个循环,能够选择要进行的操作,等操作做完,想要退出循环也是可以的,我们又知道,这个操作至少要进行一次,所以我们学过的循环发现只有do-while()循环可以用了,但对于通讯录来说他有很多操作,所以要用switch,case语句选择操作,但是又发现case后面只能跟整型表达式;所以要用一个菜单函数将这些操作与整型数字联系起来,比如在菜单函数中规定1-就是增加联系人,当switch选择1时就是增加联系人,
还需要注意一个点,也是在设计最巧妙的一个点,设置0的时候退出程序,在case后整型是0的时候break,然后到while()循环用0,就可以跳出循环,因为0为假。

在test.c 文件中写这个主函数

#include<stdio.h>
void menu()
{
	printf("***********************************\n");
	printf("****1-增加联系人   2-删除联系人****\n");
	printf("****3-修改联系人   4-查找联系人****\n");
	printf("****5-排序         6-显示通讯录****\n");
	printf("****       0-退出程序          ****\n");
	printf("***********************************\n");
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择操作->");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			break;
		case 2:
			break;
		case 3:
			break;
		case 4:
			break;
		case 5:
			break;
		case 6:
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}
🦊(2)创建一个通讯录
<创建一个结构体>
🐖想要创建一个通讯录,就要创建一个结构体,用来表示一个人的信息(姓名,性别,电话,年龄,住址),通讯录中每个人的信息,而通讯录就想当与,众多人的集合,所以可以创建一个结构体数组。
🐕而对于这个表示一个人信息的结构体,因为也只是一个结构体,我们就放在通讯录声明的文件当中,也就是 contact.h文件。
//创建一个结构体,表示一个人的信息
struct PeoInf
{
	char name[20];
	char sex[20];
	char tele[12];
	int age;
	char addr[30];
};
<创建一个通讯录>
🐖创建好这个记录每个人信息的结构体,我们现在就可以创建通讯录了,而了解本质我们就知道,通讯录就是这个结构体数组。放到代码就是 struct PeoInf data[100];这样就创建了可以存放100个人信息的通讯录。
🐕但是大家觉得这样就完事了吗?那就大错特错了,这样想如果你想要增加一个信息,你要增加那个位置,你是不知道的,所以对于通讯录来说,我们不仅要创建这个数组,也要知道这个通讯录有几个信息,这时我们创建一个 int sz;来表示通讯录中有几个人的信息,所以将这两个内容再封装成一个结构体。代码如下,因为这是封装一个通讯录结构体,所以也放在 contact.h文件中。

有了通讯录的结构体,我们就可以在主函数中创建一个通讯录。

在这里插入图片描述

🦊(3)初始化通讯录

对于创建好了通讯录,对于通讯录的内容,应该先初始化,之后才可以进行修改。下面就要进行通讯录的初始化,注意他是传的是地址,不可以传值,如果传值,他是形参的一份临时拷贝,不会改变原本的参数,也就不能初始化。

void InitContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, 100 * sizeof(struct PeoInf));
}
🦊(4)增加通讯录(功能一:增)

代码实现,在代码中用注释解释,看完代码相信你就能理解了

//增加通讯录
void AddContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	//判断通讯录的信息满了没有,就是看sz的大小
	//if (pc->sz == 100)
	//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
	//这时我们可以把通讯录中人的信息也用define定义
	if(pc -> sz == MAX)
	{
		printf("通讯录已满,无法增加数据\n"); 
		return;
		//因为通讯录已经放不下了,应该退出函数,对于这
		//个函数的返回值是void,所以不需要返回任何东西
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);	
		printf("请输入年龄:>");
		scanf("%s", &(pc->data[pc->sz].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}
🦊(5)查找通讯录(功能二:查)

跟增加通讯录一样,在注释中解释为什们要这么写

//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%s %s %s %d %s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].age,
			pc->data[i].addr);
	}
}

在这里插入图片描述

上图,是我用上面的代码增加了三个人的信息然后查看,发现他们乱七八糟,每一个都没有一一对齐,连一个表头都没有,所以我们就要将这些代码对齐然后,在创建一个表头,请看下面代码。

//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].age,
			pc->data[i].addr);
	}
}

在这里插入图片描述

🦊(6)删除通讯录-(功能三:删)

跟增加通讯录一样,在注释中解释为什们要这么写

}
//查找函数,通过名字找到这个人
int Findname(const struct Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;

}
//删除通讯录
void DelContact(struct Contact* pc)
{
	char name[NAME];
	printf("请输入删除人的名字:>");
	scanf("%s", name);
	//查找一下指定的人是否存在
	//就构造一个函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要删除的人不存在\n");
	}
	else
	{
		//删除
		int j = 0;
		for (j = ret; j < pc->sz - 1 ; j++)
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("成功删除联系人\n");
	}
}
🦊(7)查找通讯录-(功能三:查)

跟增加通讯录一样,在注释中解释为什们要这么写

//查找指定联系人
void SerchContact(const struct Contact* pc)
{
	//跟上面删除操作一样,只不过将删除变为查找
	char name[NAME];
	printf("请输入查找联系人的名字:>");
	scanf("%s", name);
	//查找一下查找的人是否存在
	//就会用上面的函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//查找联系人,找到就打印出来
		printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].tele,
			pc->data[ret].age,
			pc->data[ret].addr);
	}

🦊(8)修改通讯录-(功能三:改)

修改通讯录,就是先找到要修改的信息位置,然后再像增加通讯录一样重新录入一遍信息。

//修改指定联系人
void ModifyContact(struct Contact* pc)
{
	printf("请输入修改人的信息:>");
	char name[NAME];
	scanf("%s", name);
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//修改
		//就相当于重新录入信息
		printf("请输入名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("修改成功\n");
	}
}
🦊(9)排序通讯录

要排序通讯录,就要学会使用qsort(可以在这个地方查找信息),我也写过一遍文章介绍这么用去sort函数《用冒泡排序模拟qsort库函数》,可以参考一下
在这里插入图片描述

下面代码是按年龄排序的,只要你将排序函数改一下内容就行了

//比较
int CmpByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
	//用qsort排序
	qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
🦊(10)组装函数

我们将每个函数都写完了,但是运行起来需要在主函数中将这些函数串联起来,因此可以看下面的主函数的代码。

int main()
{
	//创建一个通讯录
	struct Contact con;
	//初始化通讯录
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请选择操作->");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			SerchContact(&con);
			break;
		case 5:
			SortContact(&con);
			break;
		case 6:
			ShowContact(&con);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}

🐕小总结

这样通讯录就写完了,看一看各各文件的代码吧

🦊test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
	printf("***********************************\n");
	printf("****1-增加联系人   2-删除联系人****\n");
	printf("****3-修改联系人   4-查找联系人****\n");
	printf("****5-排序         6-显示通讯录****\n");
	printf("****       0-退出程序          ****\n");
	printf("***********************************\n");
}
int main()
{
	//创建一个通讯录
	struct Contact con;
	//初始化通讯录
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请选择操作->");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			SerchContact(&con);
			break;
		case 5:
			SortContact(&con);
			break;
		case 6:
			ShowContact(&con);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}
🦊contact.h
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
struct PeoInfo
{
	char name[NAME];
	char sex[SEX];
	char tele[TELE];
	int age;
	char addr[ADDR];
};
//通讯录
struct Contact
{
	struct PeoInfo data[MAX];
	int sz ;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact *pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Constact* pc);


🦊 contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void InitContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	pc->sz = 0;
	memset(pc->data, 0, 100 * sizeof(struct PeoInfo));
}
//增加通讯录
void AddContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	//判断通讯录的信息满了没有,就是看sz的大小
	//if (pc->sz == 100)
	//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
	//这时我们可以把通讯录中人的信息也用define定义
	if(pc -> sz == MAX)
	{
		printf("通讯录已满,无法增加数据\n"); 
		return;
		//因为通讯录已经放不下了,应该退出函数,对于这
		//个函数的返回值是void,所以不需要返回任何东西
	}
	else
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);	
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].age,
			pc->data[i].addr);
	}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;

}
//删除通讯录
void DelContact(struct Contact* pc)
{
	char name[NAME];
	printf("请输入删除人的名字:>");
	scanf("%s", name);
	//查找一下指定的人是否存在
	//就构造一个函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要删除的人不存在\n");
	}
	else
	{
		//删除
		int j = 0;
		for (j = ret; j < pc->sz - 1 ; j++)
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("成功删除联系人\n");
	}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
	//跟上面删除操作一样,只不过将删除变为查找
	char name[NAME];
	printf("请输入查找联系人的名字:>");
	scanf("%s", name);
	//查找一下查找的人是否存在
	//就会用上面的函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//查找联系人,找到就打印出来
		printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].tele,
			pc->data[ret].age,
			pc->data[ret].addr);
	}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
	printf("请输入修改人的信息:>");
	char name[NAME];
	scanf("%s", name);
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//修改
		//就相当于重新录入信息
		printf("请输入名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("修改成功\n");
	}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
	//用qsort排序
	qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}

🐖动态通讯录

主要运用动态内存开辟的知识,不会的同学可以看看《动态内存管理(malloc free calloc realloc)》

(1)为什们要设计动态通讯录
起始就是在静态通讯录上改造,并且对于动态开辟空间的函数的运用。
静态通讯只能存放100个人,但是这样就不够灵活,为了让通讯录可以更加灵活,就设计成动态通讯录
(2)设计动态通讯录的思路
首先先做一个通讯录,用 malloc存放3个人空间,存放满了后,在用 realloc增加两个人的空间,这样就更加灵活,并且不会浪费空间。
对于这个我们创建通讯录的代码就要改变,因为 malloc创建的空间用 void*指针来接收,所以将 data[]变为指针,并且在多增加一个参数,容量参数 capacity,当 szcapacity相同时,增加2个空间。所以

🍔动态通讯录代码实现

🦊(1)创建通讯录的代码如下:

🐖

//通讯录
struct Contact
{
	struct PeoInfo *data ;
	int sz;
	int capacity;
};
🦊(2)初始化通讯录

初始化通讯录,用malloc申请3个结构体的空间

void InitContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
	if (pc == NULL)
	{
		peeor("InitContact");
		return;
	}
	else
	{
		pc -> sz = 0;
		//pc->capacity = 3;
		//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
		//这时我们可以把通讯录中人的信息也用define定义
		pc->capacity = CAPATICY;

	}
}
🦊(3)增加通讯录

对于增加那用不用改呢,答案是用的,因为当空间不够的时候就需要增加空间。

int check_capacity(struct Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//当前容量满了,现在用realloc增加容量
		struct PeoInfo* str = (struct PeoInfo*)relloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
		if (str == NULL)
		{
			printf("空间申请失败\n");
			return 0;
		}
		else
		{
			pc->data = str;
			//将申请的空间传给pc->data	
			pc->capacity = pc->capacity + 2;
			//将容量变大2.
			printf("增容成功\n");
			return 1;
		}
	}
	return 1;
}

//增加通讯录
void AddContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	//判断通讯录的信息满了没有,就是看sz的大小
	//if (pc->sz == 100)
	//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
	//这时我们可以把通讯录中人的信息也用define定义

	//我们可以封装一个函数来判断他是否需要增容
	
	int ret = check_capacity(pc);

	if (ret == 1)
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
	else
	{
		return;
	}
}
🦊(4)销毁通讯录

我们知道callocfree是同时出现的,申请了空间,就肯定要销毁空间,所以在退出函数就可以添加一个函数,销毁函数。

void DistoryContact(struct Contact* pc) 
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

🐕动态总结

动态开辟内存还是相对与来说比较简单的,希望大家先将动态内存的知识看懂,再将动态通讯录来练习学会。

🦊动态test.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
	printf("***********************************\n");
	printf("****1-增加联系人   2-删除联系人****\n");
	printf("****3-修改联系人   4-查找联系人****\n");
	printf("****5-排序         6-显示通讯录****\n");
	printf("****       0-退出程序          ****\n");
	printf("***********************************\n");
}
int main()
{
	//创建一个通讯录
	struct Contact con;
	//初始化通讯录
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请选择操作->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			SerchContact(&con);
			break;
		case 5:
			SortContact(&con);
			break;
		case 6:
			ShowContact(&con);
			break;
		case 0:
			DistoryContact(&con);
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}
🦊动态contact.h
#pragma once
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
#define CAPATICY 3
struct PeoInfo
{
	char name[NAME];
	char sex[SEX];
	char tele[TELE];
	int age;
	char addr[ADDR];
};
//通讯录
struct Contact
{
	struct PeoInfo *data ;
	int sz;
	int capacity;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact* pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Contact* pc);
//销毁通讯录
void DistoryContact(struct Contact*pc);

🦊动态contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"

void InitContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
	if (pc == NULL)
	{
		perror("InitContact");
		return;
	}
	else
	{
		pc -> sz = 0;
		//pc->capacity = 3;
		//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
		//这时我们可以把通讯录中人的信息也用define定义
		pc->capacity = CAPATICY;

	}
}
int check_capacity(struct Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//当前容量满了,现在用realloc增加容量
		struct PeoInfo* str = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
		if (str == NULL)
		{
			printf("空间申请失败\n");
			return 0;
		}
		else
		{
			pc->data = str;
			//将申请的空间传给pc->data	
			pc->capacity = pc->capacity + 2;
			//将容量变大2.
			printf("增容成功\n");
			return 1;
		}
	}
	return 1;
}

//增加通讯录
void AddContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	//判断通讯录的信息满了没有,就是看sz的大小
	//if (pc->sz == 100)
	//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
	//这时我们可以把通讯录中人的信息也用define定义

	//我们可以封装一个函数来判断他是否需要增容
	
	int ret = check_capacity(pc);

	if (ret == 1)
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
	else
	{
		return;
	}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].age,
			pc->data[i].addr);
	}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;

}
//删除通讯录
void DelContact(struct Contact* pc)
{
	char name[NAME];
	printf("请输入删除人的名字:>");
	scanf("%s", name);
	//查找一下指定的人是否存在
	//就构造一个函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要删除的人不存在\n");
	}
	else
	{
		//删除
		int j = 0;
		for (j = ret; j < pc->sz - 1; j++)
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("成功删除联系人\n");
	}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
	//跟上面删除操作一样,只不过将删除变为查找
	char name[NAME];
	printf("请输入查找联系人的名字:>");
	scanf("%s", name);
	//查找一下查找的人是否存在
	//就会用上面的函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//查找联系人,找到就打印出来
		printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].tele,
			pc->data[ret].age,
			pc->data[ret].addr);
	}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
	printf("请输入修改人的信息:>");
	char name[NAME];
	scanf("%s", name);
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//修改
		//就相当于重新录入信息
		printf("请输入名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("修改成功\n");
	}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
	//用qsort排序
	qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
void DistoryContact(struct Contact* pc) 
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

🐖文件通讯录

主要运用文件操作的知识,不会的同学可以看看《C的文件操作》

文件通讯录的需求
通讯录退出后,之前保存的信息不能改变,当下一次重新运行通讯录的时候,还能看到上次保存的信息
分析需求
退出的时候,把数据保存到文件中,当下一次运行的时候,再从文件中加载信息就可以了

🍔文件通讯录代码实现

🦊保存通讯录

首先我们要实现这个需求,在退出之前,保存通讯录。

//保存通讯录
void SaveContact(struct Contact* pc)
{
	//打开文件,用二进制的方式写
	FILE* tt = fopen("Contact.txt", "wb");
	//判断是否为空指针
	if (tt == NULL)
	{
		perror("SaveContact:fopen");
		return;
	}
	//可以写了
	for (int i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(struct PeoInfo), 1, tt);
	}

	//关闭文件
	fclose(tt);
	tt = NULL;
	return 0;
}
🦊加载通讯录

⭐将通讯录保存了,但是在打开通讯录的时候,需要能够读取文件的信息,但是这个操作在哪进行呀,我们可以在初始化通讯录的时候进行读取文件的信息操作。

static int check_capacity(struct Contact* pc);

void loadContact(struct Contact* pc)
{
	//打开文件
	FILE* tt = fopen("Contact.txt", "rb");
	if (tt == NULL)
	{
		perror("loadContact:fopen");
		return;
	}
	//读文件
	//创建一个临时结构体,当中介
	struct PeoInfo tmp = { 0 };
	//因为这是动态版本,可能涉及到增容问题
	//用他的返回值看,读取失败返回0;
	while (fread(&tmp, sizeof(struct PeoInfo), 1, tt))
	{
		//考虑增容问题
		check_capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	//关闭文件
}
🦊文件test.c

文件的代码其实大致无差,就是多了这个功能,为了以防万一还是放在这里了。欢迎大家食用。

#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
void menu()
{
	printf("***********************************\n");
	printf("****1-增加联系人   2-删除联系人****\n");
	printf("****3-修改联系人   4-查找联系人****\n");
	printf("****5-排序         6-显示通讯录****\n");
	printf("****       0-退出程序          ****\n");
	printf("***********************************\n");
}
int main()
{
	//创建一个通讯录
	struct Contact con;
	//初始化通讯录
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请选择操作->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			ModifyContact(&con);
			break;
		case 4:
			SerchContact(&con);
			break;
		case 5:
			SortContact(&con);
			break;
		case 6:
			ShowContact(&con);
			break;
		case 0:
			SaveContact(&con);
			DistoryContact(&con);
			printf("退出程序\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}
🦊文件contact.h
#pragma once
#pragma once
#include<stdio.h>
#include<string.h>
#define MAX 100
#define NAME 20
#define SEX 20
#define TELE 12
#define ADDR 30
#include<assert.h>
#include<stdlib.h>
#define CAPATICY 3
struct PeoInfo
{
	char name[NAME];
	char sex[SEX];
	char tele[TELE];
	int age;
	char addr[ADDR];
};
//通讯录
struct Contact
{
	struct PeoInfo *data ;
	int sz;
	int capacity;
};
//初始化通讯录
void InitContact(struct Contact* pc);
//增加通讯录的信息
void AddContact(struct Contact* pc);
//显示通讯录的信息
void ShowContact(const struct Contact* pc);
//删除通讯录的信息
void DelContact(struct Contact* pc);
//查找指定联系人
void SerchContact(const struct Contact* pc);
//修改指定联系人
void ModifyContact(struct Contact* pc);
//排序通讯录-按照年龄
void SortContact(struct Contact* pc);
//销毁通讯录
void DistoryContact(struct Contact*pc);
//保存通讯录
void SaveContact(struct Contact* pc);

🦊动态contact.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
static int check_capacity(struct Contact* pc);

void loadContact(struct Contact* pc)
{
	//打开文件
	FILE* tt = fopen("Contact.txt", "rb");
	if (tt == NULL)
	{
		perror("loadContact:fopen");
		return;
	}
	//读文件
	//创建一个临时结构体,当中介
	struct PeoInfo tmp = { 0 };
	//因为这是动态版本,可能涉及到增容问题
	//用他的返回值看,读取失败返回0;
	while (fread(&tmp, sizeof(struct PeoInfo), 1, tt))
	{
		//考虑增容问题
		check_capacity(pc);
		pc->data[pc->sz] = tmp;
		pc->sz++;
	}
	//关闭文件
}
void InitContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	pc->data = (struct PeoInfo*)malloc(sizeof(struct PeoInfo) * 3);
	if (pc == NULL)
	{
		perror("InitContact");
		return;
	}
	else
	{
		pc -> sz = 0;
		//pc->capacity = 3;
		//我们发现这个3,我们一直再用,可以直接用define定义Capacity = 3
		//这时我们可以把通讯录中人的信息也用define定义
		pc->capacity = CAPATICY;

	}
	//加载通讯录,让文件的信息加载到通讯录
	loadContact(pc);
}
static int check_capacity(struct Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//当前容量满了,现在用realloc增加容量
		struct PeoInfo* str = (struct PeoInfo*)realloc(pc->data, (pc->capacity + 2) * sizeof(struct PeoInfo));
		if (str == NULL)
		{
			printf("空间申请失败\n");
			return 0;
		}
		else
		{
			pc->data = str;
			//将申请的空间传给pc->data	
			pc->capacity = pc->capacity + 2;
			//将容量变大2.
			printf("增容成功\n");
			return 1;
		}
	}
	return 1;
}

//增加通讯录
void AddContact(struct Contact* pc)
{
	//防止是空指针,用断言函数
	assert(pc);
	//判断通讯录的信息满了没有,就是看sz的大小
	//if (pc->sz == 100)
	//我们发现这个100,我们一直再用,可以直接用define定义MAX=100
	//这时我们可以把通讯录中人的信息也用define定义

	//我们可以封装一个函数来判断他是否需要增容
	
	int ret = check_capacity(pc);

	if (ret == 1)
	{
		printf("请输入名字:>");
		scanf("%s", pc->data[pc->sz].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[pc->sz].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[pc->sz].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[pc->sz].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[pc->sz].addr);
		pc->sz++;
		printf("成功增加联系人\n");
	}
	else
	{
		return;
	}
}
//显示通讯录的信息
void ShowContact(const struct Contact* pc)
{
	int i = 0;
	printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].tele,
			pc->data[i].age,
			pc->data[i].addr);
	}
}
//查找函数,通过名字找到这个人
static int Findname(const struct Contact* pc, char name[])
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;

}
//删除通讯录
void DelContact(struct Contact* pc)
{
	char name[NAME];
	printf("请输入删除人的名字:>");
	scanf("%s", name);
	//查找一下指定的人是否存在
	//就构造一个函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要删除的人不存在\n");
	}
	else
	{
		//删除
		int j = 0;
		for (j = ret; j < pc->sz - 1; j++)
		{
			pc->data[j] = pc->data[j + 1];
		}
		pc->sz--;
		printf("成功删除联系人\n");
	}
}
//查找指定联系人
void SerchContact(const struct Contact* pc)
{
	//跟上面删除操作一样,只不过将删除变为查找
	char name[NAME];
	printf("请输入查找联系人的名字:>");
	scanf("%s", name);
	//查找一下查找的人是否存在
	//就会用上面的函数
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//查找联系人,找到就打印出来
		printf("%-20s\t %-20s\t %-5s\t %-12s \t%-30s\n", "名字", "性别", "电话", "年龄", "地址");
		printf("%-20s\t %-20s\t %-5s\t %-12d \t%-30s\n", pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].tele,
			pc->data[ret].age,
			pc->data[ret].addr);
	}
}
//修改指定联系人
void ModifyContact(struct Contact* pc)
{
	printf("请输入修改人的信息:>");
	char name[NAME];
	scanf("%s", name);
	int ret = Findname(pc, name);
	if (ret == -1)
	{
		printf("要查找的人不存在\n");
	}
	else
	{
		//修改
		//就相当于重新录入信息
		printf("请输入名字:>");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别:>");
		scanf("%s", pc->data[ret].sex);
		printf("请输入电话:>");
		scanf("%s", pc->data[ret].tele);
		printf("请输入年龄:>");
		scanf("%d", &(pc->data[ret].age));
		printf("请输入地址:>");
		scanf("%s", pc->data[ret].addr);
		printf("修改成功\n");
	}
}
//比较
int CmpByAge(const void* e1, const void* e2)
{
	return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age;
}
//按照年龄来排序
void SortContact(struct Contact* pc)
{
	//用qsort排序
	qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpByAge);
}
void DistoryContact(struct Contact* pc) 
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
}

//保存通讯录
void SaveContact(struct Contact* pc)
{
	//打开文件,用二进制的方式写
	FILE* tt = fopen("Contact.txt", "wb");
	//判断是否为空指针
	if (tt == NULL)
	{
		perror("SaveContact:fopen");
		return;
	}
	//可以写了
	for (int i = 0; i < pc->sz; i++)
	{
		fwrite(pc->data + i, sizeof(struct PeoInfo), 1, tt);
	}

	//关闭文件
	fclose(tt);
	tt = NULL;
	return 0;
}

🐖总结

⭐我们现在就将三种形式的通讯录写完了,相信大家肯定或多多少都有点收获,如果大家不太理解,可以私信我,看到必会,如果对大家有点帮助,希望大家一键三联,这将是我的动力。欢迎大家食用。😊😊

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

桐桐超努力

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

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

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

打赏作者

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

抵扣说明:

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

余额充值