前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言通讯录(静态&动态&文件操作/内附源码)

C语言通讯录(静态&动态&文件操作/内附源码)

作者头像
用户9645905
发布2022-11-30 08:15:39
9110
发布2022-11-30 08:15:39
举报
文章被收录于专栏:Linux学习~

目录

前言

问题描述

工具

基本框架

静态通讯录

具体实现思路与流程

准备阶段

创建联系人信息与通讯录结构体

主体

菜单界面

通讯录各功能实现

初始化通讯录(清空通讯录联系人)

添加通讯录联系人

删除通讯录联系人

查找通讯录联系人

修改联系人信息

展示通讯录联系人

排序通讯录联系人

整体效果图

附上源码

动态通讯录

通讯录结构

初始化通讯录

通讯录增容

通讯录销毁

数据保存

输出数据

输入数据(加载数据)

附上源码


前言


检验学习成果最好的方式是实践

在学习完C语言的基础知识以后,就可以简易的实现一些小项目 本文章为手把手讲解实现C语言静态通讯录(好好看,相信不会太难的说) 当你自己完成后一定成就感幸福感满满的!!

注:手把手教学,实操性非常强!!!

问题描述


用C语言实现简易通讯录

工具


vs2019

基本框架


通讯录可以用来存储100个人的信息,每个人的信息包括:姓名、性别、年龄、电话、住址

  • 提供方法:
  1. 添加联系人信息
  2. 删除指定联系人信息
  3. 查找指定联系人信息
  4. 修改指定联系人信息
  5. 显示所有联系人信息
  6. 清空所有联系人
  7. 以名字排序所有联系人

静态通讯录


 依靠数组和结构体来实现(较为基础)

具体实现思路与流程

注:参考代码部分有些地方可以根据自己的需要进行优化

准备阶段

  • 需要建立两个源文件和一个头文件:
  1. contact.h:宏定义修饰特定数据(方便维护),包含各种头文件,以及结构体与函数的声明
  2. test.c:写通讯录的整体流程和逻辑
  3. contact.c:实现通讯录各种功能函数

创建联系人信息与通讯录结构体

  • 对于联系人信息:

需要包含包括姓名、性别、年龄、电话、住址

  • 对于静态通讯录:

除了联系人,还需要记录使用的人数(方便后面功能的实现)

由此我们需要创建结构体变量

  • 参考代码:
代码语言:javascript
复制
//个人信息结构
struct Peoinfo 
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	int number[NUMBER_MAX];
	char addr[ADDR_MAX];
};

//通讯录结构
struct Contact
{
	//总联系人容量
	struct Peoinfo data[PEOINFO_MAX];
	//使用个数记录
	int sz;
};

为了方便以后维护修改,我们可以把一些数据用预处理指令来修饰

  • 参考代码:
代码语言:javascript
复制
//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX  10
#define ADDR_MAX 30

#define PEOINFO_MAX 100

主体

  • do while循环:

首先进入选择功能或者退出,选择功能后还可以继续选择,由此采用do while循环来实现整体逻辑

  • 回调函数:

对于各种功能的实现在这里一般只需要传入结构体地址来进行操作通讯录,所以参数形式都一致,返回类型也一致,所以采用回调函数便于调用函数,并且简易便捷

  • 罗盘:

一个数组,元素为函数指针(便于进行传入函数地址)

  • 参考代码:
代码语言:javascript
复制
//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo*i), struct Contact* p)
{
	pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
	int intput;
	struct Contact con;//创建一个通讯录
	InitContact(&con);//对通讯录初始化
	//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
	void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,InitContact };
	//基本逻辑循环
	do 
	{
		menu();
		printf("请输入接下来想要进行的操作:->\n");
		scanf("%d", &intput);
		if (intput > 0 && intput <=7 )
		{
			printf("成功进入所选择项操作!\n");
			Calo(table[intput],&con);//回调操作
		}
		else if (intput == 0)
		{
			printf("退出操作成功!!\n");
			break;
		}
		else
		{
			printf("输入项错误!请重新选择:->\n");
		}
	} while (intput);//intput为0则退出
}

菜单界面

这个很简单,直接上代码

  • 参考代码:
代码语言:javascript
复制
void menu()
{
	printf("**************************\n");
	printf("****1.add        2.del****\n");
	printf("****3.search  4.modify****\n");
	printf("****5.show      6.sort****\n");
	printf("****7.clear     0.exit****\n");
	printf("**************************\n");
}

通讯录各功能实现


初始化通讯录(清空通讯录联系人)

  • 注意点:
  1. 创建后我们还需要进行初始化,否则为随机值
  2. 同样的清除通讯录也相当于初始化通讯录(实现代码一致,部分细节可以自己修改)
  • 参考代码:
代码语言:javascript
复制
//初始化通讯录
void InitContact(struct Contact* p)
{
	p->sz = 0;
	//使用内存函数进行初始化数组
	memset(p->data, 0, sizeof(p->data));
}

添加通讯录联系人

  • 注意点:
  1. 首先要对通讯录判断是否已满,再进行操作
  2. 判断完后添加信息,添加成功还要记得让已使用人数+1
  • 参考代码:
代码语言:javascript
复制
//添加通讯录联系人
void AddContact(struct Contact*p)
{
	if (p->sz == PEOINFO_MAX)
	{
		printf("通讯录联系人已满,无法添加!\n");
	}
	else
	{
		printf("请输入新联系人的名字:");
		scanf("%s", p->data[p->sz].name);
		printf("请输入新联系人的年龄:");
		scanf("%d", &p->data[p->sz].age);
		printf("请输入新联系人的性别:");
		scanf("%s", p->data[p->sz].sex);
		printf("请输入新联系人的电话号码:");
		scanf("%s", p->data[p->sz].number);
		printf("请输入新联系人的地址:");
		scanf("%s", p->data[p->sz].addr);
		p->sz++;
		printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
	}
	return;
}

删除通讯录联系人

  • 注意点:

  1. 要删除联系人,首先得在通讯录里看是否存在该联系人(采用遍历),再进行操作
  2. 删除联系人我们可以选择用覆盖的方法,将后面一个联系人的信息赋值给前面一个
  3. 删除后已就得记得将已使用人数-1
  • 参考代码:
代码语言:javascript
复制
//删除通讯录联系人
void DelContact(struct Contact* p)
{
	char name[NAME_MAX];
	printf("请输入要删除的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		//字符串比较采用strcmp
		if (strcmp(name, p->data[i].name) == 0)
		{
			for (int j = i; j < p->sz - 1; j++)
			{
				//字符串赋值采用strcpy
				strcpy(p->data[j].name , p->data[j + 1].name);
				p->data[j].age = p->data[j + 1].age;
				strcpy(p->data[j].sex , p->data[j + 1].sex);
				strcpy(p->data[j].number , p->data[j + 1].number);
				strcpy(p->data[j].addr , p->data[j + 1].addr);
			}
			p->sz--;
			printf("删除通讯录联系人%s成功!\n", name);
			return;
		}
	}
	printf("在通讯录中无法查找到联系人%s!\n",name);
	return;
	
}

查找通讯录联系人

  • 注意点:

  1. 同样的我们要判断查找的联系人是否存在
  2. 如果存在我们得显示该联系人的相关信息
  • 参考代码:
代码语言:javascript
复制
//查找通讯录联系人
void SearchContact(struct Contact* p)
{
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n",p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			return;//展示后就可以退出了
		}
	}
//前面条件不符合即没有该联系人
	printf("在通讯录中无法查找到该联系人!\n");
	return;
}

修改联系人信息

  • 注意点:

  1. 同样的我们依旧要看是否存在该联系人,存在则先将信息展示
  2. 对于修改有两个方案:1.重新再输入 2.选择需要的信息修改
  3. 这里我们为了更人性化选择方案2,当然粗暴的方案1也行
  4. 对于方案2的主体逻辑我们可以用do while循环
  5. 里面再使用switch进行选择的实现
  6. 对于选择项我们可以写个选择界面出来
  • 参考代码:
代码语言:javascript
复制
//操作选项
void option()
{
	printf("**************************\n");
	printf("****1.name       2.age****\n");
	printf("****3.sex     4.number****\n");
	printf("****5.addr      0.exit****\n");
	printf("**************************\n");
}

//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
	int pos=-1;
	int intput = -1;
	ShowContact(p);
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			//找到则记录位置并进行展示
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n", p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			pos = i;
			break;
		}
	}
	//找不到则退出
	if(pos==-1)
	{
		printf("在通讯录中无法查找到该联系人!\n");
		return;
	}
	do 
	{
		option();
		printf("请输入要修改的选项:->\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			printf("请输入新的名字:\n");
			scanf("%s", &p->data[pos].name);
			break;
		case 2:
			printf("请输入新的年龄:\n");
			scanf("%d", &p->data[pos].age);
			break;
		case 3:
			printf("请输入新的性别:\n");
			scanf("%d", &p->data[pos].sex);
			break;
		case 4:
			printf("请输入新的号码:\n");
			scanf("%d", &p->data[pos].number);
			break;
		case 5:
			printf("请输入新的地址:\n");
			scanf("%d", &p->data[pos].addr);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

展示通讯录联系人

  • 注意点:

  1. 为了美观,我们可以弄一个表格出来
  2. 展示联系人信息直接遍历就好了
  • 参考代码:
代码语言:javascript
复制
//展示通讯录联系人
void ShowContact(struct Contact* p)
{
	//制表
	printf("————————————————————————————————————————————————\n");
	printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
		"name", "age", "sex", "tele", "addr");
	printf("————————————————————————————————————————————————\n");
	for (int i = 0; i < p->sz; i++)
	{
		printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n", 
			p->data[i].name,
			p->data[i].age,
			p->data[i].sex, 
			p->data[i].number,
			p->data[i].addr);
		printf("————————————————————————————————————————————————\n");
	}
	return;
}

排序通讯录联系人

  • 注意点:

  1. 对于排序我们可以使用qsort函数
  2. 对于qsort函数我们要实现对应的比较函数
  3. 选择采用的排序方式的主体逻辑于选择修改方式一致
  4. 对于选择方式界面我们可以沿用修改方式中所提到的选择界面
  • 参考代码:
代码语言:javascript
复制
//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1,const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}

int cmp_con_age(const void* e1, const void* e2)
{
	return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}

int cmp_con_sex(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}

int cmp_con_number(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}

int cmp_con_addr(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
	int intput=-1;
	do
	{
		option();
		printf("请输入排序方式的选项:\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
			ShowContact(p);
			break;
		case 2:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
			ShowContact(p);
			break;
		case 3:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
			ShowContact(p);
			break;
		case 4:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
			ShowContact(p);
			break;
		case 5:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
			ShowContact(p);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

注:如果对qsort函数有什么问题,可参考博主这篇文章: ⭐️ 高阶指针 ⭐️回调函数(回调型计算器/冒泡排序/qsort函数)

整体效果图


附上源码


  • contact.h:
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#pragma once
//特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明
#include<stdio.h>
#include<string.h>

//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX  10
#define ADDR_MAX 30

#define PEOINFO_MAX 100

//个人信息结构
struct Peoinfo 
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	int number[NUMBER_MAX];
	char addr[ADDR_MAX];
};

//通讯录结构
struct Contact
{
	//总联系人容量
	struct Peoinfo data[PEOINFO_MAX];
	//使用个数记录
	int sz;
};

//初始化通讯录(清空通讯录)
void InitContact(struct Contact* p);
//添加通讯录联系人
void AddContact(struct Contact* p);
//删除通讯录联系人
void DelContact(struct Contact* p);
//查找通讯录联系人
void SearchContact(struct Contact* p);
//修改通讯录联系人信息
void ModifyContact(struct Contact* p);
//展示通讯录联系人
void ShowContact(struct Contact* p);
//通讯录排序
void SortContact(struct Contact* p);

//操作选项
void option();

int cmp_con_name(void* e1, void* e2);

int cmp_con_age(void* e1, const void* e2);

int cmp_con_sex(const void* e1, const void* e2);

int cmp_con_number(const void* e1, const void* e2);

int cmp_con_addr(const void* e1, const void* e2);
  • tast.c:
代码语言:javascript
复制
//写通讯录的整体流程和逻辑
#include "contact.h"

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

//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo*i), struct Contact* p)
{
	pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
	int intput;
	struct Contact con;
	InitContact(&con);
	//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
	void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,InitContact };
	//基本逻辑循环
	do 
	{
		menu();
		printf("请输入接下来想要进行的操作:->\n");
		scanf("%d", &intput);
		if (intput > 0 && intput <=7 )
		{
			printf("成功进入所选择项操作!\n");
			Calo(table[intput],&con);//回调操作
		}
		else if (intput == 0)
		{
			printf("退出操作成功!!\n");
			break;
		}
		else
		{
			printf("输入项错误!请重新选择:->\n");
		}
	} while (intput);//intput为0则退出
}
  • contact.c:
代码语言:javascript
复制
//实现通讯录功能函数
#include "contact.h"

//初始化通讯录
void InitContact(struct Contact* p)
{
	p->sz = 0;
	//使用内存函数进行初始化数组
	memset(p->data, 0, sizeof(p->data));
}

//添加通讯录联系人
void AddContact(struct Contact*p)
{
	if (p->sz == PEOINFO_MAX)
	{
		printf("通讯录联系人已满,无法添加!\n");
	}
	else
	{
		printf("请输入新联系人的名字:");
		scanf("%s", p->data[p->sz].name);
		printf("请输入新联系人的年龄:");
		scanf("%d", &p->data[p->sz].age);
		printf("请输入新联系人的性别:");
		scanf("%s", p->data[p->sz].sex);
		printf("请输入新联系人的电话号码:");
		scanf("%s", p->data[p->sz].number);
		printf("请输入新联系人的地址:");
		scanf("%s", p->data[p->sz].addr);
		p->sz++;
		printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
	}
	return;
}

//删除通讯录联系人
void DelContact(struct Contact* p)
{
	char name[NAME_MAX];
	printf("请输入要删除的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		//字符串比较采用strcmp
		if (strcmp(name, p->data[i].name) == 0)
		{
			for (int j = i; j < p->sz - 1; j++)
			{
				//字符串赋值采用strcpy
				strcpy(p->data[j].name , p->data[j + 1].name);
				p->data[j].age = p->data[j + 1].age;
				strcpy(p->data[j].sex , p->data[j + 1].sex);
				strcpy(p->data[j].number , p->data[j + 1].number);
				strcpy(p->data[j].addr , p->data[j + 1].addr);
			}
			p->sz--;
			printf("删除通讯录联系人%s成功!\n", name);
			return;
		}
	}
	printf("在通讯录中无法查找到联系人%s!\n",name);
	return;
	
}

//查找通讯录联系人
void SearchContact(struct Contact* p)
{
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n",p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			return;
		}
	}
	printf("在通讯录中无法查找到该联系人!\n");
	return;
}

//操作选项
void option()
{
	printf("**************************\n");
	printf("****1.name       2.age****\n");
	printf("****3.sex     4.number****\n");
	printf("****5.addr      0.exit****\n");
	printf("**************************\n");
}

//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
	int pos=-1;
	int intput = -1;
	ShowContact(p);
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			//找到则记录位置并进行展示
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n", p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			pos = i;
			break;
		}
	}
	//找不到则退出
	if(pos==-1)
	{
		printf("在通讯录中无法查找到该联系人!\n");
		return;
	}
	do 
	{
		option();
		printf("请输入要修改的选项:->\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			printf("请输入新的名字:\n");
			scanf("%s", &p->data[pos].name);
			break;
		case 2:
			printf("请输入新的年龄:\n");
			scanf("%d", &p->data[pos].age);
			break;
		case 3:
			printf("请输入新的性别:\n");
			scanf("%d", &p->data[pos].sex);
			break;
		case 4:
			printf("请输入新的号码:\n");
			scanf("%d", &p->data[pos].number);
			break;
		case 5:
			printf("请输入新的地址:\n");
			scanf("%d", &p->data[pos].addr);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

//展示通讯录联系人
void ShowContact(struct Contact* p)
{
	//制表
	printf("————————————————————————————————————————————————\n");
	printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
		"name", "age", "sex", "tele", "addr");
	printf("————————————————————————————————————————————————\n");
	for (int i = 0; i < p->sz; i++)
	{
		printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
			p->data[i].name,
			p->data[i].age,
			p->data[i].sex,
			p->data[i].number,
			p->data[i].addr);
		printf("————————————————————————————————————————————————\n");
	}
	return;
}

//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1,const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}

int cmp_con_age(const void* e1, const void* e2)
{
	return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}

int cmp_con_sex(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}

int cmp_con_number(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}

int cmp_con_addr(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
	int intput=-1;
	do
	{
		option();
		printf("请输入排序方式的选项:\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
			ShowContact(p);
			break;
		case 2:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
			ShowContact(p);
			break;
		case 3:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
			ShowContact(p);
			break;
		case 4:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
			ShowContact(p);
			break;
		case 5:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
			ShowContact(p);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

动态通讯录


对于静态通讯录的实现我们用的是数组来存储数据 但是这样的通讯录存储的联系人容量是有限的(可能会少了,也可能大了) 而实现一个动态的通讯录(使用动态内存管理实现)就能避免这样的问题

对于动态通讯录的实现其实只要在静态通讯录上修改几个地方就可以了

注:动态内存管理知识后面我们会进行详细讲解

通讯录结构

  • 参考代码:
代码语言:javascript
复制
//通讯录结构
struct Contact
{
	//联系人总容量
	int capacity;
	//标记动态通讯录的首地址
	struct Peoinfo *data;
	//使用个数记录
	int sz;
};

初始化通讯录

使用malloc来开辟一个默认大小的动态空间

  • 参考代码:
代码语言:javascript
复制
//初始化通讯录
void InitContact(struct Contact* p)
{
	p->capacity = C_SZ;
	p->sz = 0;
	p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
	LoadContact(p);
}
代码语言:javascript
复制
//默认容量大小
#define C_SZ  2
//默认添加大小
#define PLUS  3

通讯录增容

  • 注意点:

  1. 添加通讯录联系人时进行判断(这里我们封装成一个函数)
  2. 当人数等于容量大小就增容(用realloc来调整动态开辟空间的大小)
  3. 增容失败则打印错误并结束程序
  • 参考代码:
代码语言:javascript
复制
//检查通讯录是否满
void CheakContact(struct Contact* p)
{
	if (p->sz == p->capacity)
	{
		//如果使用人数已经满了,则申请开辟更大的空间
		struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
		//ptr不为NULL则开辟成功
		if (ptr != NULL)
		{
			p->data = ptr;
			p->capacity += PLUS;
		}
		else
		{
			perror("CheakContact");//打印出错误原因
			exit(1);//结束程序
		}
	}
}

通讯录销毁

  • 注意点:

退出通讯录时我们需要将动态开辟的空间给释放(使用free函数)(否则可能造成内存泄漏)

  • 参考代码:
代码语言:javascript
复制
//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
	free(p->data);
	p -> data = NULL;
	p->capacity = 0;
	p->sz = 0;
}

数据保存


在我们的通讯录写好后,我们如果还想让通讯录能够保存我们所输入联系人数据(在退出后再打开通讯录)

注:这就需要一点文件操作的知识了(在后面的专题中会仔细讲解)

输出数据

在通讯录运行结束前,我们让通讯录联系人的数据以二进制的形式输出到指定的文件中 (即将数据保存在文件中)

  • 参考代码:
代码语言:javascript
复制
//保存通讯录数据
void SaveContact(struct Contact* p)
{
	//fopen函数:找到指定文件并返回文件的地址
	//wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		//打开失败则打印错误信息
		perror("SaveContact");
		return;
	}
	for (int i = 0; i < p->sz; i++)
	{
		//fwrite:二进制输出
		fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
	}
	//用完pf后进行释放
	fclose(pf);
	pf = NULL;
	return;
}

输入数据(加载数据)

当下一次进入通讯录将原来保存的数据输入到内存中

  • 参考代码:
代码语言:javascript
复制
//加载通讯录数据
void LoadContact(struct Contact*p)
{
	//rb:以只读的方式打开一个二进制文件
	FILE* pf = fopen("contact.txt","rb");
	if (pf == NULL)
	{
		perror("Loadcontact");
		return;
	} 
	//暂时保存数据
	struct Peoinfo tmp = { 0 };
	//fread:二进制输入(输入的数据完整则返回1,否则返回0)
	while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
	{
		//输入时判断是否需要增容
		CheakContact(p);
		p->data[p->sz] = tmp;
		p->sz++;
	}
	//释放
	fclose(pf);
	pf = NULL;
	return;
}

附上源码


  • contact.h
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#pragma once
//特定数据宏定义方式,包含各种头文件,以及结构体与函数的声明
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//信息接收最大数目(预处理指令便于维护)
#define NAME_MAX 20
#define NUMBER_MAX 11
#define SEX_MAX  10
#define ADDR_MAX 30

//默认容量大小
#define C_SZ  2
//默认添加大小
#define PLUS  3

//个人信息结构
struct Peoinfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	int number[NUMBER_MAX];
	char addr[ADDR_MAX];
};

//通讯录结构
struct Contact
{
	//联系人总容量
	int capacity;
	//标记动态通讯录的首地址
	struct Peoinfo *data;
	//使用个数记录
	int sz;
};

//初始化通讯录(清空通讯录)
void InitContact(struct Contact* p);
//添加通讯录联系人
void AddContact(struct Contact* p);
//删除通讯录联系人
void DelContact(struct Contact* p);
//查找通讯录联系人
void SearchContact(struct Contact* p);
//修改通讯录联系人信息
void ModifyContact(struct Contact* p);
//展示通讯录联系人
void ShowContact(struct Contact* p);
//通讯录排序
void SortContact(struct Contact* p);
//清除通讯录(释放)
void DestoryContact(struct Contact* p);
void SaveContact(struct Contact* p);
void LoadContact(struct Contact* p);


//操作选项
void option();

int cmp_con_name(void* e1, void* e2);

int cmp_con_age(void* e1, const void* e2);

int cmp_con_sex(const void* e1, const void* e2);

int cmp_con_number(const void* e1, const void* e2);

int cmp_con_addr(const void* e1, const void* e2);
  • contact.c
代码语言:javascript
复制
//实现通讯录功能函数
#include "contact.h"

//检查通讯录是否满
void CheakContact(struct Contact* p)
{
	if (p->sz == p->capacity)
	{
		//如果使用人数已经满了,则申请开辟更大的空间
		struct Peoinfo* ptr = (struct Peoinfo*)realloc(p->data, sizeof(struct Peoinfo) * (C_SZ + PLUS));
		//ptr不为NULL则开辟成功
		if (ptr != NULL)
		{
			p->data = ptr;
			p->capacity += PLUS;
		}
		else
		{
			perror("LoadContact");
			exit(1);
		}
	}
}

//加载通讯录数据
void LoadContact(struct Contact*p)
{
	//rb:以只读的方式打开一个二进制文件
	FILE* pf = fopen("contact.txt","rb");
	if (pf == NULL)
	{
		perror("Loadcontact");
		return;
	} 
	//暂时保存数据
	struct Peoinfo tmp = { 0 };
	//fread:二进制输入(输入的数据完整则返回1,否则返回0)
	while (fread(&tmp, sizeof(struct Peoinfo), 1, pf))
	{
		//输入时判断是否需要增容
		CheakContact(p);
		p->data[p->sz] = tmp;
		p->sz++;
	}
	//释放
	fclose(pf);
	pf = NULL;
	return;
}

//初始化通讯录
void InitContact(struct Contact* p)
{
	p->capacity = C_SZ;
	p->sz = 0;
	p->data=(struct Peoinfo*)malloc(sizeof(struct Peoinfo) * C_SZ);
	LoadContact(p);
}

//添加通讯录联系人
void AddContact(struct Contact* p)
{
	CheakContact(p);
		printf("请输入新联系人的名字:");
		scanf("%s", p->data[p->sz].name);
		printf("请输入新联系人的年龄:");
		scanf("%d", &p->data[p->sz].age);
		printf("请输入新联系人的性别:");
		scanf("%s", p->data[p->sz].sex);
		printf("请输入新联系人的电话号码:");
		scanf("%s", p->data[p->sz].number);
		printf("请输入新联系人的地址:");
		scanf("%s", p->data[p->sz].addr);
		printf("添加通讯录联系人%s成功!\n", p->data[p->sz].name);
		p->sz++;
	
	return;
}

//删除通讯录联系人
void DelContact(struct Contact* p)
{
	if (p->sz == 0)
	{
		printf("没有联系人,无法删除!\n");
		return;
	}
	char name[NAME_MAX];
	printf("请输入要删除的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		//字符串比较采用strcmp
		if (strcmp(name, p->data[i].name) == 0)
		{
			for (int j = i; j < p->sz - 1; j++)
			{
				//字符串赋值采用strcpy
				strcpy(p->data[j].name, p->data[j + 1].name);
				p->data[j].age = p->data[j + 1].age;
				strcpy(p->data[j].sex, p->data[j + 1].sex);
				strcpy(p->data[j].number, p->data[j + 1].number);
				strcpy(p->data[j].addr, p->data[j + 1].addr);
			}
			p->sz--;
			printf("删除通讯录联系人%s成功!\n", name);
			return;
		}
	}
	printf("在通讯录中无法查找到联系人%s!\n", name);
	return;

}

//查找通讯录联系人
void SearchContact(struct Contact* p)
{
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n", p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			return;
		}
	}
	printf("在通讯录中无法查找到该联系人!\n");
	return;
}

//操作选项
void option()
{
	printf("**************************\n");
	printf("****1.name       2.age****\n");
	printf("****3.sex     4.number****\n");
	printf("****5.addr      0.exit****\n");
	printf("**************************\n");
}

//修改通讯录联系人信息
void ModifyContact(struct Contact* p)
{
	int pos = -1;
	int intput = -1;
	ShowContact(p);
	char name[NAME_MAX];
	printf("请输入要进行操作的联系人名字:\n");
	scanf("%s", &name);
	for (int i = 0; i < p->sz; i++)
	{
		if (strcmp(name, p->data[i].name) == 0)
		{
			//找到则记录位置并进行展示
			printf("联系人的名字:%s\n", p->data[i].name);
			printf("联系人的年龄:%d\n", p->data[i].age);
			printf("联系人的性别:%s\n", p->data[i].sex);
			printf("联系人的电话号码:%s\n", p->data[i].number);
			printf("联系人的地址:%s\n", p->data[i].addr);
			pos = i;
			break;
		}
	}
	//找不到则退出
	if (pos == -1)
	{
		printf("在通讯录中无法查找到该联系人!\n");
		return;
	}
	do
	{
		option();
		printf("请输入要修改的选项:->\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			printf("请输入新的名字:\n");
			scanf("%s", &p->data[pos].name);
			break;
		case 2:
			printf("请输入新的年龄:\n");
			scanf("%d", &p->data[pos].age);
			break;
		case 3:
			printf("请输入新的性别:\n");
			scanf("%d", &p->data[pos].sex);
			break;
		case 4:
			printf("请输入新的号码:\n");
			scanf("%d", &p->data[pos].number);
			break;
		case 5:
			printf("请输入新的地址:\n");
			scanf("%d", &p->data[pos].addr);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

//展示通讯录联系人
void ShowContact(struct Contact* p)
{
	//制表
	printf("————————————————————————————————————————————————\n");
	printf("||%20s|\t%5s|\t%11s|\t%10s|\t%30s||\n",
		"name", "age", "sex", "tele", "addr");
	printf("————————————————————————————————————————————————\n");
	for (int i = 0; i < p->sz; i++)
	{
		printf("||%20s|\t%5d|\t%11s|\t%10s|\t%30s||\n",
			p->data[i].name,
			p->data[i].age,
			p->data[i].sex,
			p->data[i].number,
			p->data[i].addr);
		printf("————————————————————————————————————————————————\n");
	}
	return;
}

//qsort函数参数比较函数的实现
int cmp_con_name(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->name, ((struct Peoinfo*)e2)->name);
}

int cmp_con_age(const void* e1, const void* e2)
{
	return ((struct Peoinfo*)e1)->age - ((struct Peoinfo*)e2)->age;
}

int cmp_con_sex(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->sex, ((struct Peoinfo*)e2)->sex);
}

int cmp_con_number(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->number, ((struct Peoinfo*)e2)->number);
}

int cmp_con_addr(const void* e1, const void* e2)
{
	return strcmp(((struct Peoinfo*)e1)->addr, ((struct Peoinfo*)e2)->addr);
}
//通讯录排序
void SortContact(struct Contact* p)
{
	int intput = -1;
	do
	{
		option();
		printf("请输入排序方式的选项:\n");
		scanf("%d", &intput);
		switch (intput)
		{
		case 0:
			printf("退出修改成功!\n");
			break;
		case 1:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_name);
			ShowContact(p);
			break;
		case 2:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_age);
			ShowContact(p);
			break;
		case 3:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_sex);
			ShowContact(p);
			break;
		case 4:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_number);
			ShowContact(p);
			break;
		case 5:
			qsort(p->data, p->sz, sizeof(p->data[0]), cmp_con_addr);
			ShowContact(p);
			break;
		default:
			printf("输入错误,请重新输入:\n");
		}
	} while (intput);
	return;
}

//动态通讯录申请空间后不用还得释放
void DestoryContact(struct Contact* p)
{
	free(p->data);
	p -> data = NULL;
	p->capacity = 0;
	p->sz = 0;
}

//保存通讯录数据
void SaveContact(struct Contact* p)
{
	//fopen函数:找到指定文件并返回文件的地址
	//wb:以只写的方式打开(为了输出数据,打开一个二进制文件)
	FILE* pf = fopen("contact.txt", "wb");
	if (pf == NULL)
	{
		//打开失败则打印错误信息
		perror("SaveContact");
		return;
	}
	for (int i = 0; i < p->sz; i++)
	{
		//fwrite:二进制输出
		fwrite(p->data+i, sizeof(struct Peoinfo), 1, pf);
	}
	//用完pf后进行释放
	fclose(pf);
	pf = NULL;
	return;
}
  • test.c
代码语言:javascript
复制
//写通讯录的整体流程和逻辑
#include "contact.h"

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

//回调函数,需要传入函数地址(调用函数)和通讯录地址(操作通讯录)
void Calo(void(*pc)(struct Peoinfo* i), struct Contact* p)
{
	pc(p);//pc()为函数调用,参数传入p即通讯录地址
}
//主体逻辑
int main()
{
	int intput;
	struct Contact con;
	InitContact(&con);
	//写个罗盘数组,元素为函数指针(便于进行调用函数,进行传参)
	void(*table[8])(struct Contract* pc) = { 0,AddContact,DelContact,SearchContact,ModifyContact,ShowContact,SortContact,DestoryContact };
	//基本逻辑循环
	do
	{
		menu();
		printf("请输入接下来想要进行的操作:->\n");
		scanf("%d", &intput);
		if (intput > 0 && intput <= 7)
		{
			printf("成功进入所选择项操作!\n");
			Calo(table[intput], &con);//回调操作
		}
		else if (intput == 0)
		{
			SaveContact(&con);
			Calo(table[7], &con);
			printf("退出操作成功!!\n");
			break;
		}
		else
		{
			printf("输入项错误!请重新选择:->\n");
		}
	} while (intput);//intput为0则退出
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 问题描述
  • 工具
  • 基本框架
  • 静态通讯录
    • 具体实现思路与流程
      • 准备阶段
        • 创建联系人信息与通讯录结构体
          • 主体
            • 菜单界面
            • 通讯录各功能实现
              • 初始化通讯录(清空通讯录联系人)
                • 添加通讯录联系人
                  • 删除通讯录联系人
                    • 查找通讯录联系人
                      • 修改联系人信息
                        • 展示通讯录联系人
                          • 排序通讯录联系人
                            • 整体效果图
                            • 附上源码
                            • 动态通讯录
                              • 通讯录结构
                                • 初始化通讯录
                                  • 通讯录增容
                                    • 通讯录销毁
                                    • 数据保存
                                      • 输出数据
                                        • 输入数据(加载数据)
                                        • 附上源码
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档