C语言 - 通讯录详解

通讯录

1. 基本思路

  1. 用宏定义经常出现的变量,如数组的大小,通讯录的初识容量等。
  2. 定义两个结构体,用一个结构体记录某一个联系人的所有信息,另外一个结构体记录所有的联系人,当前通讯录的大小以及通讯录能存多少人。
  3. 通讯录的功能有增删差改,排序,以及打印。
  4. 文章中文字解释较少,主要解释都在代码中。

2.代码实现

2.1 定义各种结构体

在这里插入图片描述


// 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20  // 名字的字符串的最大长度
#define SEX_MAX 20   // 性别的字符串的最大长度
#define TELE_MAX 20  // 电话的最大字符串的长度
#define ADDR_MAX 20  // 住址的最大字符串的长度

#define CON_INIT 3 // 通讯录的初识大小

// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{
	char name[NAME_MAX]; // 每个人的姓名
	char sex[SEX_MAX];   // 每个人的性别
	int age;             // 每个人的年龄
	char tele[TELE_MAX]; // 每个人的电话
	char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;


// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{
	// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间
	PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息
	int size; // 表示当前通讯录存了多少人的信息
	int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;

2.2 创建结构体并进行初始化

在这里插入图片描述

// 定义一个通讯录结构体
	Contact con;
	// 对该通讯录结构体进行初始化
	// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改
	// 并且传结构体的地址节省空间且效率更高
	InitContact(&con); 

在这里插入图片描述


// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{
	// 此时通讯录是空的,应先为通讯录分配空间
	pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);

	// 如果分配成功,将通讯录的size设为0,capacity设为初识大小
	pc->size = 0;
	pc->capacity = CON_INIT;
}

2.3 打印菜单,模拟用户的选择

菜单如下:
在这里插入图片描述


void menu()
{
	printf("请输入你的选择\n");
	printf("***** 1. add     2. del    *****\n");
	printf("***** 3. search  4. modify *****\n");
	printf("***** 5. sort    6. print  *****\n");
	printf("*****        0. exit       *****\n");

	return;
}

用户的选择功能如下
在这里插入图片描述

// 定义input,用户输入input进行选择
	int input;
	do
	{
		menu(); // 打印菜单,提示用户进行选择
		scanf("%d", &input); // 用户输入input进行选择
		switch (input) // 用户选择后进入不同的功能
		{
		case ADD: // 增加联系人
			AddContact(&con);
			break;
		case DEL: // 删除联系人
			DeleteContact(&con);
			break;
		case SEARCH: // 查找联系人
			SearchContact(&con);
			break;
		case MODIFY: // 改变联系人
			ModifyContact(&con);
			break;
		case SORT:  // 对联系人进行排序
			SortContact(&con);
			break;
		case PRINT: // 打印所有联系人的信息
			PrintContact(&con);
			break;
		case EXIT: // 退出程序并销毁
			DestroyContact(&con);
			break;
		}
	} while (input);

用枚举常量来表示各种选项
在这里插入图片描述

2.4 增加联系人

在每次增加联系人前,都要进行检查是否需要扩容的操作,若需要则进行扩容

在这里插入图片描述

void AddContact(Contact* pc)
{
	// 判断通讯录是否已满,若满,进行扩容
	if (pc->size == pc->capacity)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			return;
		}
		pc->data = tmp;

		// 若扩容成功,增大capacity
		pc->capacity *= 2;
	}

	// 输入要添加的联系人的信息
	// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息
	printf("请输入名字\n");
	scanf("%s", pc->data[pc->size].name);
	printf("请输入性别\n");
	scanf("%s", pc->data[pc->size].sex);
	printf("请输入年龄\n");
	scanf("%d", &pc->data[pc->size].age);
	printf("请输入电话\n");
	scanf("%s", pc->data[pc->size].tele);
	printf("请输入住址\n");
	scanf("%s", pc->data[pc->size].addr);

	pc->size++; // 将存入的联系人的数量加1

	// 添加成功后,向用户展示新的通讯录
	PrintContact(pc);
}

2.5 删除联系人

在这里插入图片描述


void DeleteContact(Contact* pc)
{
	printf("请输入要删除的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 定义一个新函数find,用来查找是否有这个联系人
	// 如果有,返回联系人的下标,如果没有,返回-1
	int ret = find(pc, name);

	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		for (int i = ret; i < pc->size - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		pc->size--;
	}

	return;
}

这里还要定义一个find函数来找该联系人,如果找到返回该联系人的下标,如果找不到则返回-1。

在这里插入图片描述


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

2.6 查找联系人

查找时利用find函数来查找
在这里插入图片描述


void SearchContact(Contact* pc)
{
	printf("请输入要查找的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 利用已经定义的find函数进行查找
	int ret = find(pc, name);

	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		// 如果找到,打印该联系人的信息,首先打印五个标题
		printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",
			pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].age,
			pc->data[ret].tele,
			pc->data[ret].addr);
	}

	return;
}

2.7 修改联系人

在这里插入图片描述


void ModifyContact(Contact* pc)
{
	printf("请输入要修改的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 利用find函数进行查找
	int ret = find(pc, name);
	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		printf("请输入名字\n");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别\n");
		scanf("%s", pc->data[ret].sex);
		printf("请输入年龄\n");
		scanf("%d", &pc->data[ret].age);
		printf("请输入电话\n");
		scanf("%s", pc->data[ret].tele);
		printf("请输入住址\n");
		scanf("%s", pc->data[ret].addr);
	}

	return;
}

2.8 对通讯录进行升序排序

在这里插入图片描述

void SortContact(Contact* pc)
{
	// 这里采用升序排列
	// 且采用冒泡排序的方式进行排列
	for (int i = 0; i < pc->size; i++)
	{
		for (int j = 0; j < pc->size - 1 - i; i++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				char tmp[20];
				strcpy(tmp, pc->data[j].name);
				strcpy(pc->data[j].name, pc->data[j + 1].name);
				strcpy(pc->data[j + 1].name, tmp);
			}
		}
	}

	printf("排序成功\n");
}

2.9 打印通讯录

在这里插入图片描述


// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{
	// 首先打印五个标题
	printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
	// 然后用for循环打印所有联系人的信息
	for (int i = 0; i < pc->size; i++)
	{
		printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr
		);
	}
}

2.10 结束程序并销毁通讯录

在这里插入图片描述

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

3. 文件改进版本

利用文件实现
当通讯录退出的时候,把信息写到文件。
当通讯录初始化后,加载文件的信息到通讯录中。

3.1 每次关闭通讯录将信息保存到文件中

在每次销毁通讯录之前将通讯录的信息保存到文件中
在这里插入图片描述
在这里插入图片描述

3.2 每次初始化通讯录后将文件中信息加载到通讯录上

在这里插入图片描述
在这里插入图片描述
CheckCapacity函数中的内容直接拷贝AddContact函数中的数组扩容即可
在这里插入图片描述

4.程序整体代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 用宏替换后续经常出现的数字,便于程序的维护
#define NAME_MAX 20  // 名字的字符串的最大长度
#define SEX_MAX 20   // 性别的字符串的最大长度
#define TELE_MAX 20  // 电话的最大字符串的长度
#define ADDR_MAX 20  // 住址的最大字符串的长度

#define CON_INIT 3 // 通讯录的初识大小

// 定义每个人的信息为一个结构体,同时为了方便表示,采用typedef
typedef struct PeoInfo
{
	char name[NAME_MAX]; // 每个人的姓名
	char sex[SEX_MAX];   // 每个人的性别
	int age;             // 每个人的年龄
	char tele[TELE_MAX]; // 每个人的电话
	char addr[ADDR_MAX]; // 每个人的住址
}PeoInfo;


// 定义整个通讯录为一个结构体,同时为了方便表示,采用typedef
typedef struct Contact
{
	// 为了节省空间,采用动态内存分配的方法来获取整个通讯录的空间
	PeoInfo* data; // 结构体指针,可通过运算访问到所有人的信息
	int size; // 表示当前通讯录存了多少人的信息
	int capacity; // 表示该通讯录总共可以存多少人的信息
}Contact;

// 使用枚举常量来表示用户的各种选择
enum Option
{
	EXIT,
	ADD,
	DEL,
	SEARCH,
	MODIFY,
	SORT,
	PRINT
};


void SaveContact(Contact* pc)
{
	// 以读的形式打开文件
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	// 将通讯录以二进制的形式保存到文件中
	// 由于联系人的信息有很多,所以这里可以采用for循环
	for (int i = 0; i < pc->size; i++)
	{
		fwrite(pc->data + i, sizeof(PeoInfo), 1, pf);
	}

	// 关闭文件
	fclose(pf);
	pf = NULL;
}

CheckCapacity(Contact* pc)
{
	// 判断通讯录是否已满,若满,进行扩容
	if (pc->size == pc->capacity)
	{
		PeoInfo* tmp = (PeoInfo*)realloc(pc, sizeof(PeoInfo) * 2);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			return;
		}
		pc->data = tmp;

		// 若扩容成功,增大capacity
		pc->capacity *= 2;
	}
}

void LoadContact(Contact* pc)
{
	// 以读的形式打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen");
		return;
	}

	// 将文件中的内容加载到通讯录中
	// 这里用fread函数,当fread函数读取的联系人信息数为0时,说明读取结束
	PeoInfo tmp = { 0 }; // 定义一个联系人信息的结构体,便于读取
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		CheckCapacity(pc); // 在这个函数中查看数组是否需要扩容,若需要,则扩容
		pc->data[pc->size] = tmp;
		pc->size++;
	}

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

// 在这个函数内部对通讯录结构体进行初始化
void InitContact(Contact* pc)
{
	// 此时通讯录是空的,应先为通讯录分配空间
	pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * CON_INIT);

	// 如果分配成功,将通讯录的size设为0,capacity设为初识大小
	pc->size = 0;
	pc->capacity = CON_INIT;

	LoadContact(pc);

	return;
}

void menu()
{
	printf("请输入你的选择\n");
	printf("***** 1. add     2. del    *****\n");
	printf("***** 3. search  4. modify *****\n");
	printf("***** 5. sort    6. print  *****\n");
	printf("*****        0. exit       *****\n");

	return;
}

// 在这个函数内打印所有联系人的信息
void PrintContact(Contact* pc)
{
	// 首先打印五个标题
	printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
	// 然后用for循环打印所有联系人的信息
	for (int i = 0; i < pc->size; i++)
	{
		printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].tele,
			pc->data[i].addr
		);
	}
}

void AddContact(Contact* pc)
{
	CheckCapacity(pc);

	// 输入要添加的联系人的信息
	// 这里pc->data为结构体数组,pc->data[pc->size]为其中的元素,也就是某一个联系人的信息
	printf("请输入名字\n");
	scanf("%s", pc->data[pc->size].name);
	printf("请输入性别\n");
	scanf("%s", pc->data[pc->size].sex);
	printf("请输入年龄\n");
	scanf("%d", &pc->data[pc->size].age);
	printf("请输入电话\n");
	scanf("%s", pc->data[pc->size].tele);
	printf("请输入住址\n");
	scanf("%s", pc->data[pc->size].addr);

	pc->size++; // 将存入的联系人的数量加1

	// 添加成功后,向用户展示新的通讯录
	PrintContact(pc);
}

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

void DeleteContact(Contact* pc)
{
	printf("请输入要删除的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 定义一个新函数find,用来查找是否有这个联系人
	// 如果有,返回联系人的下标,如果没有,返回-1
	int ret = find(pc, name);

	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		for (int i = ret; i < pc->size - 1; i++)
		{
			pc->data[i] = pc->data[i + 1];
		}
		pc->size--;
	}

	return;
}

void SearchContact(Contact* pc)
{
	printf("请输入要查找的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 利用已经定义的find函数进行查找
	int ret = find(pc, name);

	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		// 如果找到,打印该联系人的信息,首先打印五个标题
		printf("%-10s\t%-10s\t%-5s\t%-15s\t%-10s\n", "姓名", "性别", "年龄", "电话", "住址");
		printf("%-10s\t%-10s\t%-5d\t%-15s\t%-10s\n",
			pc->data[ret].name,
			pc->data[ret].sex,
			pc->data[ret].age,
			pc->data[ret].tele,
			pc->data[ret].addr);
	}

	return;
}

void ModifyContact(Contact* pc)
{
	printf("请输入要修改的联系人的名字\n");
	char name[20];
	scanf("%s", name);
	// 利用find函数进行查找
	int ret = find(pc, name);
	if (ret == -1)
	{
		printf("没有找到该联系人\n");
	}
	else
	{
		printf("请输入名字\n");
		scanf("%s", pc->data[ret].name);
		printf("请输入性别\n");
		scanf("%s", pc->data[ret].sex);
		printf("请输入年龄\n");
		scanf("%d", &pc->data[ret].age);
		printf("请输入电话\n");
		scanf("%s", pc->data[ret].tele);
		printf("请输入住址\n");
		scanf("%s", pc->data[ret].addr);
	}

	return;
}

void SortContact(Contact* pc)
{
	// 这里采用升序排列
	// 且采用冒泡排序的方式进行排列
	for (int i = 0; i < pc->size; i++)
	{
		for (int j = 0; j < pc->size - 1 - i; i++)
		{
			if (strcmp(pc->data[j].name, pc->data[j + 1].name) > 0)
			{
				char tmp[20];
				strcpy(tmp, pc->data[j].name);
				strcpy(pc->data[j].name, pc->data[j + 1].name);
				strcpy(pc->data[j + 1].name, tmp);
			}
		}
	}

	printf("排序成功\n");
}

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

int main()
{
	// 定义一个通讯录结构体
	Contact con;
	// 对该通讯录结构体进行初始化
	// 此后应该传结构体的地址而不是结构体,原因是要对结构体作出修改
	// 并且传结构体的地址节省空间且效率更高
	InitContact(&con); 

	// 定义input,用户输入input进行选择
	int input;
	do
	{
		menu(); // 打印菜单,提示用户进行选择
		scanf("%d", &input); // 用户输入input进行选择
		switch (input) // 用户选择后进入不同的功能
		{
		case ADD: // 增加联系人
			AddContact(&con);
			break;
		case DEL: // 删除联系人
			DeleteContact(&con);
			break;
		case SEARCH: // 查找联系人
			SearchContact(&con);
			break;
		case MODIFY: // 改变联系人
			ModifyContact(&con);
			break;
		case SORT:  // 对联系人进行排序
			SortContact(&con);
			break;
		case PRINT: // 打印所有联系人的信息
			PrintContact(&con);
			break;
		case EXIT: // 退出程序并销毁
			//在销毁之前将通讯录内的信息保存到文件中
			SaveContact(&con);
			DestroyContact(&con);
			break;
		}
	} while (input);

	return 0;
}


  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zzu_ljk

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

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

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

打赏作者

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

抵扣说明:

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

余额充值