【C语言】动态顺序表实现通讯录(附完整源码)

文章目录

  • 一、contact.h
  • 二、contact.c
    • 1. 显示菜单
    • 2. 初始化通讯录
    • 3. 加载文件信息到通讯录
    • 4. 检查容量
    • 5. 增加联系人
    • 6. 显示联系人
    • 7. 查找联系人
    • 8. 删除联系人
    • 9. 修改联系人
    • 10. 清空联系人
    • 11. 按名字排序
    • 12. 保存通讯录
    • 13. 销毁通讯录
  • 三、test.c
  • 四、完整源码
    • 1. 头文件 contact.h
    • 2. 源文件 contact.c
    • 3. 源文件 test.c

一、contact.h

//利用define方便统一修改常量
#define NAME_MAX 20
#define SEX_MAX 10
#define ADDR_MAX 30
#define TELE_MAX 12
#define DEFAULT_SZ 3
#define INC_SZ 2
//包含必要的头文件
#include
#include
#include
#include

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];//地址
	char tele[TELE_MAX];//电话
}PeoInfo;

//定义通讯录
typedef struct Contact
{
	PeoInfo* data;//指向 存放人的信息的空间
	int sz;//已存放信息个数
	int capacity;//当前通讯录的最大容量
}Contact;

//利用枚举表示选项,提高代码可读性
enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,
	SHOW,
	EMPTY,
	SORT
};

//自定义函数的函数原型
void menu();//显示菜单
void InitContact(Contact* pc);//初始化通讯录
void check_capacity(Contact* pc);//检查容量
void DestroyContact(Contact* pc);//销毁通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示联系人
void DelContact(Contact* pc);//删除指定联系人
int FindByName(Contact* pc, char name[]);
void SearchContact(const Contact* pc);//查找联系人
void ModifyContact(Contact* pc);//修改联系人
void EmptyContact(Contact* pc);//清空联系人
void SortContact(Contact* pc);//按名字排序
void SaveContact(Contact* pc);//保存通讯录到文件中
void LoadContact(Contact* pc);//加载文件信息到通讯录中

二、contact.c

1. 显示菜单

void menu()
{
	printf("*******************************\n");
	printf("***    1.add     2.delete   ***\n");
	printf("***    3.search  4.modify   ***\n");
	printf("***    5.show    6.empty   ***\n");
	printf("***    7.sort    0.exit     ***\n");
	printf("*******************************\n");
}

显示菜单的所有选项,总共八个功能:

  • 增加联系人
  • 删除联系人
  • 查找联系人
  • 修改联系人
  • 显示联系人
  • 清空联系人
  • 按名字排序
  • 退出通讯录

2. 初始化通讯录

void InitContact(Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	pc->sz = 0;//初始化,已存放信息个数归0
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	//利用calloc动态分配内存并初始化0,起始地址用ptr接收
	if (ptr == NULL)//判断内存开辟是否成功
	{
		perror("InitContact : : calloc");//显示报错信息
		return;
	}
	pc->data = ptr;//内存开辟成功后,将地址赋给指向人的信息的指针data
	pc->capacity = DEFAULT_SZ;//初始化当前通讯录容量

	//加载已有文件信息到通讯录中
	LoadContact(pc);
}

3. 加载文件信息到通讯录

void LoadContact(Contact* pc)
{
	//从文件中读数据
	//1.打开文件
	FILE* pf = fopen("Contact.txt", "rb");//以只读的形式打开二进制文件
	if (NULL == pf)//检验文件是否打开成功
	{
		perror("LoadContact");//显示报错信息
	}
	else
	{
		//2.读文件
		PeoInfo tmp = { 0 };//创建临时变量
		int i = 0;
		//一个一个读取人的信息到临时变量中
		for (i = 0; fread(&tmp, sizeof(PeoInfo), 1, pf); i++, pc->sz++)
		{
			check_capacity(pc);//检查容量
			pc->data[i] = tmp;//从临时变量中拷贝信息到通讯录
		}
		printf("加载成功\n");
		//3.关闭文件
		fclose(pf);
		pf = NULL;//指针置空
	}
}

4. 检查容量

void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)//判断已存放信息个数是否达到当前最大容量
	{
		//利用realloc增容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));//增容后内存的起始地址用ptr接收
		if (ptr == NULL)//判断增容是否成功
		{
			perror("check_capacity : : realloc");//显示报错信息
			return;
		}
		pc->data = ptr;//增容成功后将地址赋给指向人的信息的指针data
		pc->capacity += INC_SZ;//更新容量大小
		printf("增容成功\n");
	}
}

5. 增加联系人

void AddContact(Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	check_capacity(pc);//增加联系人前,先检查容量

	//增加一个人的信息
	//利用sz巧妙访问想要的位置
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);//name是数组名,就是地址
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));//age是变量,需要取地址
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);//数组名,就是地址
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);//数组名,就是地址
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);//数组名,就是地址
	printf("添加成功\n");
	pc->sz++;//已存放信息个数加一
}

6. 显示联系人

void ShowContact(const Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	printf("%-20s\t%-4s\t%-10s\t%-30s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");//格式化输出字符串
	
	int i = 0;
	for (i = 0; i < pc->sz; i++)//利用循环遍历通讯录中每一个人的信息
	{
		//格式化打印
		printf("%-20s\t%-4d\t%-10s\t%-30s\t%-12s\n",
			pc->data[i].name, 
			pc->data[i].age, 
			pc->data[i].sex, 
			pc->data[i].addr, 
			pc->data[i].tele);
	}
}

7. 查找联系人

void SearchContact(const Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	char name[NAME_MAX] = { 0 };//创建与联系人名字同样大小的字符数组
	printf("请输入要查找的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);//调用函数查找,详见下文
	if (-1 == ret)
	{
		printf("未找到\n");
		return;
	}

	//格式化打印查找的联系人的信息
	printf("%-20s\t%-4s\t%-10s\t%-30s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-10s\t%-30s\t%-12s\n",
			pc->data[ret].name,
			pc->data[ret].age,
			pc->data[ret].sex,
			pc->data[ret].addr,
			pc->data[ret].tele);
}//利用ret访问查找到的信息
int FindByName(const Contact* pc, char name[])
{
	assert(pc);//断言,检验传入指针是否有效
	int i = 0;
	for (i = 0; i < pc->sz; i++)//利用循环遍历联系人信息
	{
		if (0 == strcmp(pc->data[i].name, name))//比较字符串
		{
			return i;//返回查找到的联系人信息下标,用于访问
		}
	}
	return -1;
}

关于strcmp函数的具体用法可以参考我的另一篇博客:【C语言】常用的字符串函数和内存函数【超全总结】

8. 删除联系人

void DelContact(Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	char name[NAME_MAX] = { 0 };//创建与联系人名字同样大小的字符数组
	if (pc->sz == 0)//判断通讯录是否为空
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);

	int ret = FindByName(pc,name);//调用函数查找
	if (ret == -1)
	{
		printf("未找到\n");
		return;
	}
	//找到后
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)//利用循环遍历联系人信息
	{
		pc->data[i] = pc->data[i + 1];
		//从查找到要删除的联系人信息后面开始,每一个人的信息都往前覆盖一位
		//从而达到删除联系人的效果
	}
	pc->sz--;//更新已存放的信息个数
	printf("删除成功\n");
}

9. 修改联系人

void ModifyContact(Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	char name[NAME_MAX] = { 0 };//创建与联系人名字同样大小的字符数组
	printf("请输入要修改的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);//调用函数查找
	if (-1 == ret)
	{
		printf("未找到\n");
		return;
	}
	//找到后,重新输入信息,覆盖原来的信息,从而修改联系人信息
	printf("请输入名字:>");
	scanf("%s", pc->data[ret].name);//name是数组名,就是地址
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[ret].age));//age是变量,需要取地址
	printf("请输入性别:>");
	scanf("%s", pc->data[ret].sex);//数组名,就是地址
	printf("请输入地址:>");
	scanf("%s", pc->data[ret].addr);//数组名,就是地址
	printf("请输入电话:>");
	scanf("%s", pc->data[ret].tele);//数组名,就是地址
	printf("修改成功\n");
}

10. 清空联系人

void EmptyContact(Contact* pc)
{
	InitContact(pc);//要清空通讯录,只需再初始化通讯录即可
	printf("清空成功\n");
}

11. 按名字排序

void SortContact(Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效

	//冒泡排序
	for (int i = 0; i < pc->sz - 1; i++)//趟数
	{
		for (int j = 0; j < pc->sz - i - 1; j++)//一趟中两两判断的次数
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				PeoInfo tmp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp;
			}
		}
	}
	//打印
	ShowContact(pc);
}

12. 保存通讯录

void SaveContact(Contact* pc)
{
	//把数据写到文件中
	//1.打开文件
	FILE* pf = fopen("Contact.txt", "wb");
	//以二进制只写的方式打开文件
	//若文件不存在则自动新建一个二进制文件并打开
	
	if (NULL == pf)//判断文件是否成功打开
	{
		perror("SaveContact");//显示报错信息
	}
	else//文件已成功打开
	{
		//2.写入
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
		}
		//3.关闭文件
		fclose(pf);
		pf = NULL;//指针置空
		printf("文件保存成功\n");
	}
}

有关文件操作的内容,可以参考我的另外一篇博客:【C语言】文件操作必知必会

13. 销毁通讯录

void DestroyContact(Contact* pc)
{
	free(pc->data);//释放动态开辟的内存,即存放信息的空间
	pc->data = NULL;//指向人的信息的指针置空
	pc->capacity = 0;//容量归0
	pc->sz = 0;//存放信息个数归0
	pc = NULL;//指向通讯录的指针置空
}

三、test.c

#include"contact.h"//包含自定义的头文件

int main()
{
	int input = 0;

	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);

	do
	{
		menu();//显示菜单
		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);//只有输入0才能终止循环

	return 0;
}

四、完整源码

1. 头文件 contact.h

#define NAME_MAX 20
#define SEX_MAX 10
#define ADDR_MAX 30
#define TELE_MAX 12
#define DEFAULT_SZ 3
#define INC_SZ 2
//包含必要的头文件
#include
#include
#include
#include

//人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char addr[ADDR_MAX];//地址
	char tele[TELE_MAX];//电话
}PeoInfo;

//定义通讯录
typedef struct Contact
{
	PeoInfo* data;//指向 存放人的信息的空间
	int sz;//已存放信息个数
	int capacity;//当前通讯录的最大容量
}Contact;

//利用枚举表示选项,提高代码可读性
enum Option
{
	EXIT,//0
	ADD,//1
	DEL,//2
	SEARCH,//3
	MODIFY,
	SHOW,
	EMPTY,
	SORT
};

//自定义函数的函数原型
void menu();//显示菜单
void InitContact(Contact* pc);//初始化通讯录
void check_capacity(Contact* pc);//检查容量
void DestroyContact(Contact* pc);//销毁通讯录
void AddContact(Contact* pc);//增加联系人
void ShowContact(const Contact* pc);//显示联系人
void DelContact(Contact* pc);//删除指定联系人
int FindByName(Contact* pc, char name[]);
void SearchContact(const Contact* pc);//查找联系人
void ModifyContact(Contact* pc);//修改联系人
void EmptyContact(Contact* pc);//清空联系人
void SortContact(Contact* pc);//按名字排序
void SaveContact(Contact* pc);//保存通讯录到文件中
void LoadContact(Contact* pc);//加载文件信息到通讯录中

2. 源文件 contact.c

#include"contact.h"

void menu()
{
	printf("*******************************\n");
	printf("***    1.add     2.delete   ***\n");
	printf("***    3.search  4.modify   ***\n");
	printf("***    5.show    6.empty   ***\n");
	printf("***    7.sort    0.exit     ***\n");
	printf("*******************************\n");
}

void InitContact(Contact* pc)
{
	assert(pc);
	pc->sz = 0;
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact : : calloc");
		return;
	}
	pc->data = ptr;
	pc->capacity = DEFAULT_SZ;

	//加载文件信息到通讯录中
	LoadContact(pc);
}

void check_capacity(Contact* pc)
{
	if (pc->sz == pc->capacity)
	{
		//增容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));
		if (ptr == NULL)
		{
			perror("check_capacity : : realloc");
			return;
		}
		pc->data = ptr;
		pc->capacity += INC_SZ;
		printf("增容成功\n");
	}
}

void AddContact(Contact* pc)
{
	assert(pc);
	check_capacity(pc);

	//增加一个人的信息
	printf("请输入名字:>");
	scanf("%s", pc->data[pc->sz].name);//name是数组名,就是地址
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].age));//age是变量,需要取地址
	printf("请输入性别:>");
	scanf("%s", pc->data[pc->sz].sex);//数组名,就是地址
	printf("请输入地址:>");
	scanf("%s", pc->data[pc->sz].addr);//数组名,就是地址
	printf("请输入电话:>");
	scanf("%s", pc->data[pc->sz].tele);//数组名,就是地址
	printf("添加成功\n");
	pc->sz++;
}

//显示联系人
void ShowContact(const Contact* pc)
{
	assert(pc);//断言,检验传入指针是否有效
	printf("%-20s\t%-4s\t%-10s\t%-30s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");//格式化输出字符串

	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-10s\t%-30s\t%-12s\n",
			pc->data[i].name,
			pc->data[i].age,
			pc->data[i].sex,
			pc->data[i].addr,
			pc->data[i].tele);
	}
}

int FindByName(const Contact* pc, char name[])
{
	assert(pc);
	int i = 0;
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(pc->data[i].name, name))
		{
			return i;
		}
	}
	return -1;
}

void DelContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	if (pc->sz == 0)
	{
		printf("通讯录为空,无法删除\n");
		return;
	}
	printf("请输入要删除的人的名字:>");
	scanf("%s", name);

	int ret = FindByName(pc,name);
	if (ret == -1)
	{
		printf("未找到\n");
		return;
	}
	int i = 0;
	for (i = ret; i < pc->sz - 1; i++)
	{
		pc->data[i] = pc->data[i + 1];
	}
	pc->sz--;
	printf("删除成功\n");
}

void SearchContact(const Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要查找的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("未找到\n");
		return;
	}

	//打印查找的联系人的信息
	printf("%-20s\t%-4s\t%-10s\t%-30s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-10s\t%-30s\t%-12s\n",
			pc->data[ret].name,
			pc->data[ret].age,
			pc->data[ret].sex,
			pc->data[ret].addr,
			pc->data[ret].tele);
}

void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改的人的名字:>");
	scanf("%s", name);
	int ret = FindByName(pc, name);
	if (-1 == ret)
	{
		printf("未找到\n");
		return;
	}
	printf("请输入名字:>");
	scanf("%s", pc->data[ret].name);//name是数组名,就是地址
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[ret].age));//age是变量,需要取地址
	printf("请输入性别:>");
	scanf("%s", pc->data[ret].sex);//数组名,就是地址
	printf("请输入地址:>");
	scanf("%s", pc->data[ret].addr);//数组名,就是地址
	printf("请输入电话:>");
	scanf("%s", pc->data[ret].tele);//数组名,就是地址
	printf("修改成功\n");
}

void EmptyContact(Contact* pc)
{
	InitContact(pc);
	printf("清空成功\n");
}

//按名字排序
void SortContact(Contact* pc)
{
	assert(pc);

	//冒泡排序
	for (int i = 0; i < pc->sz - 1; i++)//趟数
	{
		for (int j = 0; j < pc->sz - i - 1; j++)//一趟中两两判断的次数
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				PeoInfo tmp = pc->data[j];
				pc->data[j] = pc->data[j + 1];
				pc->data[j + 1] = tmp;
			}
		}
	}

	//打印
	ShowContact(pc);
}

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

void SaveContact(Contact* pc)
{
	//把数据写到文件中
	//1.打开文件
	FILE* pf = fopen("Contact.txt", "wb");
	if (NULL == pf)
	{
		perror("SaveContact");
	}
	else
	{
		//2.写入
		int i = 0;
		for (i = 0; i < pc->sz; i++)
		{
			fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
		}
		//3.关闭文件
		fclose(pf);
		pf = NULL;
		printf("文件保存成功\n");
	}
}

void LoadContact(Contact* pc)
{
	//从文件中读数据
	//1.打开文件
	FILE* pf = fopen("Contact.txt", "rb");
	if (NULL == pf)
	{
		perror("LoadContact");
	}
	else
	{
		//2.读文件
		PeoInfo tmp = { 0 };
		int i = 0;
		for (i = 0; fread(&tmp, sizeof(PeoInfo), 1, pf); i++, pc->sz++)
		{
			check_capacity(pc);
			pc->data[i] = tmp;
		}
		printf("加载成功\n");
		//3.关闭文件
		fclose(pf);
		pf = NULL;
	}
}

3. 源文件 test.c

#include"contact.h"

int main()
{
	int input = 0;

	//创建通讯录
	Contact con;
	//初始化通讯录
	InitContact(&con);

	do
	{
		menu();
		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;
}

你可能感兴趣的:(C语言小项目,c语言,开发语言,数据结构)