【C语言】手把手教你实现通讯录(静态+动态+文件存储版本)

目录

一、具体实现步骤 

(一)创建工程

​编辑

(二)设计存储数据的结构形式

(三)函数功能设计及实现

初始化函数

 添加通讯录信息的函数

展示通讯录内容

删除通讯录内容

查找通讯录内容

修改通讯录内容

清空通讯录

排序通讯录 按名字 升序

(四)实现菜单 测试函数功能 

二、源码附录

(一)静态版本

1.1.contact.h

1.2.contact.c

1.3.test.c

(二)动态版本

2.1.contact.h

2.2.contact.c

2.3.test.c

(三)文件存储版本

3.1.contact.h

3.2.contact.c

3.3.test.c


环境:Visual Studio2022

实现语言:C语言

一、具体实现步骤 

(一)创建工程

step1.首先我们需要创建一个工程,打开编译器,点击创建新项目

 step2.选择空项目,点击下一步

step3.为项目起一个合适的名字 ,我这里就叫Contact ,然后点击创建

step4.来到新的页面后 找到解决方案资源管理器 分别右键源文件、头文件 选择添加新建项,分别建好contact.h   contact.c   test.c 三个文件

解释:

contact.h

  • 统一包含 工程所需要包含的头文件 
  • 将未来可能修改大小的值 定义成 标识符常量
  • 声明自定义的数据类型
  • 通讯录功能 函数的声明

contact.c   

  • 实现通讯录功能的函数 

test.c

  • 用来测试通讯录的功能

(二)设计存储数据的结构形式

首先设计一下存储数据的结构形式,我们采用的是顺序表,简单点说,就是用数组存储每一个人的信息,再加上一个用来记录当前通讯录中包含多少个人的信息的变量 就构成了一个通讯录,我们将它自定义为一个通讯录类型的数据

//声明并类型重命名 一个通讯录的 结构体类型
//该类型包含
//一个 Peoinfo类型的 结构体数组 data[] 用来存放每一个人的信息
//一个 sz变量 用来记录当前通讯录中包含多少个人的信息
typedef struct Contact
{
	Peoinfo data[DATA_MAX];
	int sz;
}Contact;

 然后我们发现,每个人的信息包括:姓名、性别、年龄、电话、住址 这些条目,所以我们将每个人的信息打包。

//声明 一个 可以包含一条人信息的 结构体类型
//并进行类型重命名 重命名为Peoinfo
typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peoinfo;

(三)函数功能设计及实现

  • 添加联系人信息
  • 删除指定联系人信息
  • 查找指定联系人信息
  • 修改指定联系人信息
  • 显示所有联系人信息
  • 清空所有联系人
  • 以名字排序所有联系人

 设计函数的形参实现,在头文件中声明函数

//初始化通讯录
void InitContact(Contact* con);
//向通讯录中添加信息
void AddContact(Contact* con);
//展示通讯录内容
void ShowContact(Contact* con);
//删除通讯录内容
void DelContact(Contact* con);
//查找通讯录内容
void SearchContact(Contact* con);
//修改通讯录内容
void ModifyContact(Contact* con);
//清空通讯录
void EmptyContact(Contact* con);
//排序通讯录 按名字 升序
void SortContact(Contact* con);

在Contact.c中具体实现这些函数

1.初始化函数

初始化时,传的是结构体Con的地址,用结构体指针pc来接收,里面并用aasert断言一下,以便知道是哪行出了问题,它属于一种暴力检查。再用memset函数,把通讯录里面的信息条数置为0,即把sz记录当前数量置为0。

void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data,0,sizeof(pc->data));
}
 2.添加通讯录信息的函数
  • 用结构体指针变量pc接收,直接用标准输入输出函数,去录入信息,如名字,用结构体指针pc访问数组,并用pc访问成员变量sz找到数组下标,再.name找到名字。
  • 里面要注意再用scanf的时候 成员变量如果是数组,数组名就是首元素地址,不用再加&取地址运算符,而成员变量如果是整型变量要加取地址符号。
  • 最后用sz++,记录已录人数,并打印成功添加。
void AddContact(Contact* pc)
{
	assert(pc);
	//增加联系人之前 应先判断通讯录是否满了
	if (pc->sz == DATA_MAX)
	{
		printf("通讯录已满,不能执行添加操作!\n");
		return;
	}
	//增加信息
	printf("联系人姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("联系人性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("联系人电话:");
	scanf("%s", pc->data[pc->sz].tel);
	printf("联系人住址:");
	scanf("%s", pc->data[pc->sz].addr);

	//增加成功
	pc->sz++;
	printf("增加成功!\n");
}
3.展示通讯录内容
  • 还是要实现对齐保持上下一致,用for循环直接打印全部信息。
void ShowContact(Contact* pc)
{
	assert(pc);
	//打印之前判断通讯录中是否存在联系人
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需打印!\n");
	}
	int i = 0;
	printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tel,
			pc->data[i].addr);
	}
}
4.删除通讯录内容
  • 在里面要先创建name数组,来存放要删除的名字,之后需要判断要删除的人是否存在还需创建Find_name()函数来判断,并用ret接收。
  • 找到之后用for循环把后面的元素左移。
  • 最后人数减一。
  • 此函数如下:
void DelContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要删除的联系人姓名:");
	scanf("%s", name);
	//查找联系人是否存在 
	//封装FindPeoInfo_ByName()函数 如果存在返回下标 如果不存在返回-1
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int i = 0;
		//为什么是pc->sz-1?
		//1000个元素 下标0~999 
		//i < pc->sz-1 意味着只能访问到 i(max)=998 
		//pc->data[998] = pc->data[999];才不会发生下标越界
		for (i = ret; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		//若删除最后一个元素 最后一个元素不用被覆盖 pc->sz--即可
		pc->sz--;
		printf("删除成功!\n");
	}
}

解析里面用到的FindPeoInfo_ByName():
除了用pc接收,还要接收删除name的地址,在里面用for循环,遍历查找,里面再用if判断语句来判断,用strcmp比较函数来比较所输入名字和通讯录名字是否一样。
如果一样返回下标。没有找到返回-1。

//遍历data数组 寻找姓名字符串一样的数组下标
//如果存在返回下标 如果不存在返回-1
static int FindPeoInfo_ByName(char* name, Contact* pc)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}

查找通讯录内容

  • 前面的步骤和删除步骤一样,需要判断搜索的人是否存在,如果存在打印。
  • 为了让信息显示的更美观和直观,用对齐方式来实现且上下两个打印函数保持一致,负号表示右对齐。
void SearchContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要查找的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		printf("该联系人存在:\n");
		printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[ret].name, pc->data[ret].sex,
			pc->data[ret].age, pc->data[ret].tel,
			pc->data[ret].addr);
	}
}
5.修改通讯录内容
void modify_meun()
{
	printf("************************************************\n");
	printf("***1.姓名 2.性别 3.年龄 4.电话 5.住址 0.退出****\n");
	printf("************************************************\n");
}
enum modify_option
{
	modify_exit,
	NAME,
	SEX,
	AGE,
	TEL,
	ADDR
};
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要修改的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int modify_option = 0;
		printf("该联系人存在!\n");
		modify_meun();
		printf("请选择你要修改的信息:");
		scanf("%d", &modify_option);
		switch (modify_option)
		{
			case NAME:
				printf("修改联系人姓名:");
				scanf("%s", pc->data[ret].name);
				break;
			case SEX:
				printf("修改联系人性别:");
				scanf("%s", pc->data[ret].sex);
				break;
			case AGE:
				printf("修改联系人年龄:");
				scanf("%d", &(pc->data[ret].age));
				break;
			case TEL:
				printf("修改联系人电话:");
				scanf("%s", pc->data[ret].tel);
				break;
			case ADDR:
				printf("修改联系人住址:");
				scanf("%s", pc->data[ret].addr);
				break;
			case modify_exit:
				printf("退出修改!\n");
				break;
			default:
				printf("不存在该选项,请重新选择!\n");
				break;
		}
	}
}
6.清空通讯录
void EmptyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz != 0)
	{
		InitContact(pc);
	}
}
7.排序通讯录 按名字 升序
void SortContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < pc->sz - 1 - i; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				Peoinfo temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("排序完成!\n");
}

(四)实现菜单 测试函数功能 

二、源码附录

(一)静态版本

1.1.contact.h

//工程所需要包含的头文件
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>

//将未来可能修改大小的值 定义成 标识符常量
#define NAME_MAX 20
#define SEX_MAX 5
#define TEL_MAX 20
#define ADDR_MAX 20
#define DATA_MAX 1000


//声明 一个 可以包含一条人信息的 结构体类型
//并进行类型重命名 重命名为Peoinfo
typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peoinfo;

//声明并类型重命名 一个通讯录的 结构体类型
//该类型包含
//一个 Peoinfo类型的 结构体数组 data[] 用来存放每一个人的信息
//一个 sz变量 用来记录当前通讯录中包含多少个人的信息
typedef struct Contact
{
	Peoinfo data[DATA_MAX];
	int sz;
}Contact;

//包含通讯录功能函数的声明

//初始化通讯录
void InitContact(Contact* con);
//向通讯录中添加信息
void AddContact(Contact* con);
//展示通讯录内容
void ShowContact(Contact* con);
//删除通讯录内容
void DelContact(Contact* con);
//查找通讯录内容
void SearchContact(Contact* con);
//修改通讯录内容
void ModifyContact(Contact* con);
//清空通讯录
void EmptyContact(Contact* con);
//排序通讯录 按名字 升序
void SortContact(Contact* con);

1.2.contact.c

//实现通讯录功能的函数
#include"contact.h"


void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	memset(pc->data,0,sizeof(pc->data));
}


void AddContact(Contact* pc)
{
	assert(pc);
	//增加联系人之前 应先判断通讯录是否满了
	if (pc->sz == DATA_MAX)
	{
		printf("通讯录已满,不能执行添加操作!\n");
		return;
	}
	//增加信息
	printf("联系人姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("联系人性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("联系人电话:");
	scanf("%s", pc->data[pc->sz].tel);
	printf("联系人住址:");
	scanf("%s", pc->data[pc->sz].addr);

	//增加成功
	pc->sz++;
	printf("增加成功!\n");
}


void ShowContact(Contact* pc)
{
	assert(pc);
	//打印之前判断通讯录中是否存在联系人
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需打印!\n");
	}
	int i = 0;
	printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tel,
			pc->data[i].addr);
	}
}


//遍历data数组 寻找姓名字符串一样的数组下标
//如果存在返回下标 如果不存在返回-1
static int FindPeoInfo_ByName(char* name, Contact* pc)
{
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}


void DelContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要删除的联系人姓名:");
	scanf("%s", name);
	//查找联系人是否存在 
	//封装FindPeoInfo_ByName()函数 如果存在返回下标 如果不存在返回-1
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int i = 0;
		//为什么是pc->sz-1?
		//1000个元素 下标0~999 
		//i < pc->sz-1 意味着只能访问到 i(max)=998 
		//pc->data[998] = pc->data[999];才不会发生下标越界
		for (i = ret; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		//若删除最后一个元素 最后一个元素不用被覆盖 pc->sz--即可
		pc->sz--;
		printf("删除成功!\n");
	}
}


void SearchContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要查找的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		printf("该联系人存在:\n");
		printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[ret].name, pc->data[ret].sex,
			pc->data[ret].age, pc->data[ret].tel,
			pc->data[ret].addr);
	}
}
void modify_meun()
{
	printf("************************************************\n");
	printf("***1.姓名 2.性别 3.年龄 4.电话 5.住址 0.退出****\n");
	printf("************************************************\n");
}
enum modify_option
{
	modify_exit,
	NAME,
	SEX,
	AGE,
	TEL,
	ADDR
};
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要修改的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int modify_option = 0;
		printf("该联系人存在!\n");
		modify_meun();
		printf("请选择你要修改的信息:");
		scanf("%d", &modify_option);
		switch (modify_option)
		{
			case NAME:
				printf("修改联系人姓名:");
				scanf("%s", pc->data[ret].name);
				break;
			case SEX:
				printf("修改联系人性别:");
				scanf("%s", pc->data[ret].sex);
				break;
			case AGE:
				printf("修改联系人年龄:");
				scanf("%d", &(pc->data[ret].age));
				break;
			case TEL:
				printf("修改联系人电话:");
				scanf("%s", pc->data[ret].tel);
				break;
			case ADDR:
				printf("修改联系人住址:");
				scanf("%s", pc->data[ret].addr);
				break;
			case modify_exit:
				printf("退出修改!\n");
				break;
			default:
				printf("不存在该选项,请重新选择!\n");
				break;
		}
	}
}


void EmptyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz != 0)
	{
		InitContact(pc);
	}
}


void SortContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < pc->sz - 1 - i; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				Peoinfo temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("排序完成!\n");
}

1.3.test.c

用来测试通讯录的功能
#include"contact.h"
void meun()
{
	printf("***************************\n");
	printf("*****1.ADD    2.DEL   *****\n");
	printf("*****3.SEARCH 4.MODIFY*****\n");
	printf("*****5.SHOW   6.EMPTY *****\n");
	printf("*****7.SORT   0.EXIT  *****\n");
	printf("***************************\n");
}
enum Option
{
	EXIT,//0
	ADD,//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	EMPTY,
	SORT
};
int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		meun();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case EMPTY:
			EmptyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			printf("退出通讯录!\n");
			break;
		default:
			printf("没有可执行的操作,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

(二)动态版本

2.1.contact.h

//工程所需要包含的头文件
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>

//将未来可能修改大小的值 定义成 标识符常量
#define NAME_MAX 20
#define SEX_MAX 5
#define TEL_MAX 20
#define ADDR_MAX 20
#define DATA_MAX 1000

//Dynamic版本
#define DEFAULT_CAPACITY 3 
#define DEFAULT_INC 2 

//声明 一个 可以包含一条人信息的 结构体类型
//并进行类型重命名 重命名为Peoinfo
typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peoinfo;


//Dynamic版本
//声明并类型重命名 一个通讯录的 结构体类型
//该类型包含
//一个 Peoinfo类型的 data指针,用来接收calloc开辟的空间 (用来存放人的信息)
//一个 sz变量 用来记录当前通讯录中包含多少个人的信息
//一个 capacity变量 了用来记录当前通讯录的容量
typedef struct Contact
{
	Peoinfo *data;
	int sz;
	int capacity;
}Contact;

//通讯录功能函数的声明

//初始化通讯录
void InitContact(Contact* con);

//向通讯录中添加信息
void AddContact(Contact* con);

//展示通讯录内容
void ShowContact(Contact* con);

//删除通讯录内容
void DelContact(Contact* con);

//查找通讯录内容
void SearchContact(Contact* con);

//修改联系人内容
void ModifyContact(Contact* con);

//清空通讯录
void EmptyContact(Contact* con);

//排序通讯录 按名字 升序
void SortContact(Contact* con);

//检查容量
void CheckCapacity(Contact* con);

//销毁通讯录
void DestroyContact(Contact* con);

2.2.contact.c

//实现通讯录功能的函数
#include"contact.h"

//Dynamic版本
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	//一开始默认通讯录的容量可以存 DEFAULT_CAPACITY 个人的信息
	pc->capacity = DEFAULT_CAPACITY;
	//所以开辟 pc->capacity个 大小为sizeof(Peoinfo)个字节的空间
	pc->data = (Peoinfo*)calloc(pc->capacity,sizeof(Peoinfo));
	//需要判断是否开辟成功
	if (pc->data == NULL)
	{
		perror("InitContact->calloc");
		return;
	}
}
//检查通讯录容量
void CheckCapacity(Contact* pc)
{
	//如果容量满了,调用realloc函数对动态开辟内存大小做调整
	if (pc->sz == pc->capacity)
	{
		Peoinfo* ptr = realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peoinfo));
		//判断realloc是否开辟成功 若开辟成功再赋给pc->data
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功!\n");
		}
		//若不成功 报错
		else
		{
			perror("AddContact->realloc:");
			return;
		}
	}
}

//用来释放动态开辟的内存空间 
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}

//Dynamic 版本
void AddContact(Contact* pc)
{
	assert(pc);
	//增加联系人之前 应先判断通讯录是否还有容量
	CheckCapacity(pc);
	
	//增加信息
	printf("联系人姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("联系人性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("联系人电话:");
	scanf("%s", pc->data[pc->sz].tel);
	printf("联系人住址:");
	scanf("%s", pc->data[pc->sz].addr);

	//增加成功
	pc->sz++;
	printf("增加成功!\n");
}

void ShowContact(Contact* pc)
{
	assert(pc);
	//打印之前判断通讯录中是否存在联系人
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需打印!\n");
		return;
	}
	int i = 0;
	printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tel,
			pc->data[i].addr);
	}
}


//遍历data数组 寻找姓名字符串一样的数组下标
//如果存在返回下标 如果不存在返回-1
static int FindPeoInfo_ByName(char* name, Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}


void DelContact(Contact* pc)
{
	assert(pc);
	//首先判断通讯录是否有信息可以删除
	if (pc->sz = 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	char name[NAME_MAX];
	printf("请输入你要删除的联系人姓名:");
	scanf("%s", name);
	//查找联系人是否存在 
	//封装FindPeoInfo_ByName()函数 如果存在返回下标 如果不存在返回-1
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
		return;
	}
	//删除联系人
	else
	{
		int i = 0;
		//为什么是pc->sz-1?
		//1000个元素 下标0~999 
		//i < pc->sz-1 意味着只能访问到 i(max)=998 
		//pc->data[998] = pc->data[999];才不会发生下标越界
		for (i = ret; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		//若删除最后一个元素 最后一个元素不用被覆盖 pc->sz--即可
		pc->sz--;
		printf("删除成功!\n");
	}
}


void SearchContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入你要查找的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		printf("该联系人存在:\n");
		printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[ret].name, pc->data[ret].sex,
			pc->data[ret].age, pc->data[ret].tel,
			pc->data[ret].addr);
	}
}
void modify_meun()
{
	printf("************************************************\n");
	printf("***1.姓名 2.性别 3.年龄 4.电话 5.住址 0.退出****\n");
	printf("************************************************\n");
}
enum modify_option
{
	modify_exit,
	NAME,
	SEX,
	AGE,
	TEL,
	ADDR
};
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要修改的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int modify_option = 0;
		printf("该联系人存在!\n");
		modify_meun();
		printf("请选择你要修改的信息:");
		scanf("%d", &modify_option);
		switch (modify_option)
		{
		case NAME:
			printf("修改联系人姓名:");
			scanf("%s", pc->data[ret].name);
			break;
		case SEX:
			printf("修改联系人性别:");
			scanf("%s", pc->data[ret].sex);
			break;
		case AGE:
			printf("修改联系人年龄:");
			scanf("%d", &(pc->data[ret].age));
			break;
		case TEL:
			printf("修改联系人电话:");
			scanf("%s", pc->data[ret].tel);
			break;
		case ADDR:
			printf("修改联系人住址:");
			scanf("%s", pc->data[ret].addr);
			break;
		case modify_exit:
			printf("退出修改!\n");
			break;
		default:
			printf("不存在该选项,请重新选择!\n");
			break;
		}
	}
}


void EmptyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz != 0)
	{
		InitContact(pc);
		return;
	}
}


void SortContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < pc->sz - 1 - i; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				Peoinfo temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("排序完成!\n");
}

2.3.test.c

#include"contact.h"
void meun()
{
	printf("***************************\n");
	printf("*****1.ADD    2.DEL   *****\n");
	printf("*****3.SEARCH 4.MODIFY*****\n");
	printf("*****5.SHOW   6.EMPTY *****\n");
	printf("*****7.SORT   0.EXIT  *****\n");
	printf("***************************\n");
}
enum Option
{
	EXIT,//0
	ADD,//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	EMPTY,
	SORT
};
int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		meun();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DEL:
			DelContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case EMPTY:
			EmptyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			DestroyContact(&con);
			printf("退出通讯录!\n");
			break;
		default:
			printf("没有可执行的操作,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

(三)文件存储版本

3.1.contact.h

//工程所需要包含的头文件
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>

//将未来可能修改大小的值 定义成 标识符常量
#define NAME_MAX 20
#define SEX_MAX 5
#define TEL_MAX 20
#define ADDR_MAX 20
#define DATA_MAX 1000

//Dynamic版本新增
#define DEFAULT_CAPACITY 3 
#define DEFAULT_INC 2 

//声明 一个 可以包含一条人信息的 结构体类型
//并进行类型重命名 重命名为Peoinfo
typedef struct Peoinfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}Peoinfo;

//Dynamic版本
//声明并类型重命名 一个通讯录的 结构体类型
//该类型包含
//一个 Peoinfo类型的 data指针,用来接收calloc开辟的空间 (用来存放人的信息)
//一个 sz变量 用来记录当前通讯录中包含多少个人的信息
//一个 capacity变量 用来记录当前通讯录的容量
typedef struct Contact
{
	Peoinfo *data;
	int sz;
	int capacity;
}Contact;

//通讯录功能函数的声明

//初始化通讯录
void InitContact(Contact* con);

//向通讯录中添加信息
void AddContact(Contact* con);

//展示通讯录内容
void ShowContact(Contact* con);

//删除通讯录内容
void DelContact(Contact* con);

//查找通讯录内容
void SearchContact(Contact* con);

//修改联系人内容
void ModifyContact(Contact* con);

//清空通讯录
void EmptyContact(Contact* con);

//排序通讯录 按名字 升序
void SortContact(Contact* con);

//检查容量
void CheckCapacity(Contact* con);

//销毁通讯录
void DestroyContact(Contact* con);

//保存通讯录内容到文件
void SaveContact(Contact* con);

//加载文件中的通讯录信息
void LoadContact(Contact* con);

3.2.contact.c

#define _CRT_SECURE_NO_WARNINGS 1
//实现通讯录功能的函数
#include"contact.h"

void CheckCapacity(Contact* pc);
void LoadContact(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("Contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	//读取文件
	//创建Peoinfo类型的临时变量,读取的数据暂放在Peoinfo中
	Peoinfo tmp = { 0 };
	//成功读取数据返回1 进入循环 ,返回0 没有读到 循环停止
	//读取一个放入一个
	while (fread(&tmp, sizeof(Peoinfo), 1, pf))
	{
		//放入数据前,先检测通讯录中的容量大小capacity
		CheckCapacity(pc);
		//放入数据
		pc->data[pc->sz] = tmp;
		//放入数据后 记录包含多少个人信息的变量+1
		pc->sz++;
	}
	
	//关闭文件
	fclose(pf);
	pf = NULL;
}


//File版本的初始化函数
void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	//一开始默认通讯录的容量可以存 DEFAULT_CAPACITY 个人的信息
	pc->capacity = DEFAULT_CAPACITY;
	//所以开辟 pc->capacity个 大小为sizeof(Peoinfo)个字节的空间
	pc->data = (Peoinfo*)calloc(pc->capacity, sizeof(Peoinfo));
	//需要判断是否开辟成功
	if (pc->data == NULL)
	{
		perror("InitContact->calloc");
		return;
	}
	//加载文件中的信息
	//加载pc所指向的通讯录中信息
	LoadContact(pc);

}



void CheckCapacity(Contact* pc)
{
	//如果容量满了,调用realloc函数对动态开辟内存大小做调整
	if (pc->sz == pc->capacity)
	{
		Peoinfo* ptr = realloc(pc->data, (pc->capacity + DEFAULT_INC) * sizeof(Peoinfo));
		//判断realloc是否开辟成功 若开辟成功再赋给pc->data
		if (ptr != NULL)
		{
			pc->data = ptr;
			pc->capacity += DEFAULT_INC;
			printf("增容成功!\n");
		}
		//若不成功 报错
		else
		{
			perror("AddContact->realloc:");
			return;
		}
	}
}
//用来释放动态开辟的内存空间 
void DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->sz = 0;
	pc->capacity = 0;
}
//Dynamic 版本
void AddContact(Contact* pc)
{
	assert(pc);
	//增加联系人之前 应先判断通讯录是否还有容量
	CheckCapacity(pc);
	
	//增加信息
	printf("联系人姓名:");
	scanf("%s", pc->data[pc->sz].name);
	printf("联系人性别:");
	scanf("%s", pc->data[pc->sz].sex);
	printf("联系人年龄:");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("联系人电话:");
	scanf("%s", pc->data[pc->sz].tel);
	printf("联系人住址:");
	scanf("%s", pc->data[pc->sz].addr);

	//增加成功
	pc->sz++;
	printf("增加成功!\n");
}

void ShowContact(Contact* pc)
{
	assert(pc);
	//打印之前判断通讯录中是否存在联系人
	if (pc->sz == 0)
	{
		printf("通讯录为空,无需打印!\n");
		return;
	}
	int i = 0;
	printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[i].name, pc->data[i].sex,
			pc->data[i].age, pc->data[i].tel,
			pc->data[i].addr);
	}
}


//遍历data数组 寻找姓名字符串一样的数组下标
//如果存在返回下标 如果不存在返回-1
static int FindPeoInfo_ByName(char* name, Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (strcmp(name, pc->data[i].name) == 0)
		{
			return i;
		}
	}
	return -1;
}


void DelContact(Contact* pc)
{
	assert(pc);
	//首先判断通讯录是否有信息可以删除
	if (pc->sz = 0)
	{
		printf("通讯录为空,无法删除!\n");
		return;
	}
	char name[NAME_MAX];
	printf("请输入你要删除的联系人姓名:");
	scanf("%s", name);
	//查找联系人是否存在 
	//封装FindPeoInfo_ByName()函数 如果存在返回下标 如果不存在返回-1
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
		return;
	}
	//删除联系人
	else
	{
		int i = 0;
		//为什么是pc->sz-1?
		//1000个元素 下标0~999 
		//i < pc->sz-1 意味着只能访问到 i(max)=998 
		//pc->data[998] = pc->data[999];才不会发生下标越界
		for (i = ret; i < pc->sz - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		//若删除最后一个元素 最后一个元素不用被覆盖 pc->sz--即可
		pc->sz--;
		printf("删除成功!\n");
	}
}


void SearchContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX];
	printf("请输入你要查找的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		printf("该联系人存在:\n");
		printf("%-20s %-20s %-20s %-20s %-20s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-20s %-20s %-20d %-20s %-20s\n",
			pc->data[ret].name, pc->data[ret].sex,
			pc->data[ret].age, pc->data[ret].tel,
			pc->data[ret].addr);
	}
}
void modify_meun()
{
	printf("************************************************\n");
	printf("***1.姓名 2.性别 3.年龄 4.电话 5.住址 0.退出******\n");
	printf("************************************************\n");
}
enum modify_option
{
	modify_exit,
	NAME,
	SEX,
	AGE,
	TEL,
	ADDR
};
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[20];
	printf("请输入你要修改的联系人姓名:");
	scanf("%s", name);
	int ret = FindPeoInfo_ByName(name, pc);
	if (ret == -1)
	{
		printf("该联系人不存在!\n");
	}
	else
	{
		int modify_option = 0;
		printf("该联系人存在!\n");
		modify_meun();
		printf("请选择你要修改的信息:");
		scanf("%d", &modify_option);
		switch (modify_option)
		{
		case NAME:
			printf("修改联系人姓名:");
			scanf("%s", pc->data[ret].name);
			break;
		case SEX:
			printf("修改联系人性别:");
			scanf("%s", pc->data[ret].sex);
			break;
		case AGE:
			printf("修改联系人年龄:");
			scanf("%d", &(pc->data[ret].age));
			break;
		case TEL:
			printf("修改联系人电话:");
			scanf("%s", pc->data[ret].tel);
			break;
		case ADDR:
			printf("修改联系人住址:");
			scanf("%s", pc->data[ret].addr);
			break;
		case modify_exit:
			printf("退出修改!\n");
			break;
		default:
			printf("不存在该选项,请重新选择!\n");
			break;
		}
	}
}


void EmptyContact(Contact* pc)
{
	assert(pc);
	if (pc->sz != 0)
	{
		InitContact(pc);
		return;
	}
}


void SortContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < pc->sz - 1 - i; j++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				Peoinfo temp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = temp;
			}
		}
	}
	printf("排序完成!\n");
}


void SaveContact(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("Contact.txt", "wb");
	//判断文件是否打开成功,若打开失败报错
	if (pf == NULL)
	{
		perror("SaveContact");
		return;
	}

	//写文件:将通讯录的内容写入文件
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		//一种写法
		//fwrite(&(pc->data[i]), sizeof(Peoinfo), 1, pf);
		//另一种写法
		fwrite(pc->data+i, sizeof(Peoinfo), 1, pf);
	}
	
	//关闭文件
	fclose(pf);
	pf = NULL;
}

3.3.test.c

#include"contact.h"
void meun()
{
	printf("***************************\n");
	printf("*****1.ADD    2.DEL   *****\n");
	printf("*****3.SEARCH 4.MODIFY*****\n");
	printf("*****5.SHOW   6.EMPTY *****\n");
	printf("*****7.SORT   0.EXIT  *****\n");
	printf("***************************\n");
}
enum Option
{
	EXIT,//0
	ADD,//1
	DEL,
	SEARCH,
	MODIFY,
	SHOW,
	EMPTY,
	SORT
};
int main()
{
	int input = 0;
	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);
	do
	{
		meun();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		//添加联系人
		case ADD:
			AddContact(&con);
			break;
		//删除联系人
		case DEL:
			DelContact(&con);
			break;
		//查找联系人
		case SEARCH:
			SearchContact(&con);
			break;
		//修改联系人
		case MODIFY:
			ModifyContact(&con);
			break;
		//展示通讯录
		case SHOW:
			ShowContact(&con);
			break;
		//清空通讯录
		case EMPTY:
			EmptyContact(&con);
			break;
		//排序
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			//保存信息到文件
			SaveContact(&con);
			DestroyContact(&con);
			printf("退出通讯录!\n");
			break;
		default:
			printf("没有可执行的操作,请重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

  • 56
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 41
    评论
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值