C语言实现动态管理通讯录信息系统(静态通讯录plus版)

news2024/7/6 18:07:37

文章目录

  • 前言:
  • 一.动态管理思想
    • 1.通讯录结构体声明发生变化
    • 2.通讯录结构体初始化发生变化
    • 3.通讯录能够动态增容
    • 4.通讯录销毁数据
  • 二.优化通讯录可持续读写信息
    • 1.保存通讯录中的信息到文件中
    • 2.加载文件信息到通讯录中
  • 三.源码
    • 1.text.c
    • 2.contact.c
    • 3.contact.h

前言:

1.0版本–>静态通讯录

  • 在上一个静态版本中,我们在使用的时候会发现有诸多不便,比如通讯录可以存放的信息总量是固定不变的,比如当我们打开通讯录写入数据后,保存再打开时上一次写入的数据消失不见了,本篇将详细讲解如何使用动态内存管理和文件操作解决这两个问题:

一.动态管理思想

1.通讯录结构体声明发生变化

  • 首先,在静态通讯录中,结构体的大小是固定好的,比如有20个空间,我们就没法存放30个人的信息,但是只存放3个人的信息那又太浪费了。
  • 所以,我们让通讯录结构体一开始只有3个空间,满了后就增容2个空间,这样可以提高空间利用率,需要用到动态内存管理:

原来1.0静态版本:


#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 10
#define ADDRESS_MAX 30
#define TELEPHONE_MAX 12
 
 
//定义一个结构体类型,这里存放人的信息
typedef struct PeoInfo
{
	char name[NAME_MAX];
	int age;
	char sex[SEX_MAX];
	char address[ADDRESS_MAX];
	char telephone[TELEPHONE_MAX];
}PeoInfo;
//所有人的都放到这里
typedef struct Contact
{
	PeoInfo data[MAX];
	int size;
}Contact;

现在2.0动态版本:

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12

#define DEFAULT_SZ 3
#define INC_SZ 2

//人的信息
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;

解析:原来的data[MAX]数组存放信息,其大小MAX是固定的,现在通讯录随时会增容,故要改为指针指向每个人的信息,sz值随着通讯录中的人数动态增长:
在这里插入图片描述

2.通讯录结构体初始化发生变化

为每个结构体动态分配内存空间

//初始化通讯录
void InitContact(Contact* pc)
{
	assert(pc);
	PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SIZE, sizeof(PeoInfo));
	if (ptr == NULL)
	{
		perror("InitContact::calloc");
		return;
	}
	pc->data = ptr;
	pc->size = 0;
	pc->capacity = DEFAULT_SIZE;
}

3.通讯录能够动态增容

对静态通讯录来说,满了就不能继续添加了。
我们为解决这一问题,给动态通讯录封装一个增容函数,以实现动态扩容:

//检查当前通讯录的容量:
//sz满了才增加容量,不满就没必要增加
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");
	}
}

解析:只有当sz(即当前存在通信录中的人数)等于当前最大容量capaciity,即存满了的时候,才扩容,我们定义INC_SZ为每次再增加多少容量

4.通讯录销毁数据

由于现在的内存空间都是动态分配的,所以数据也都在堆区上,那么每次关闭通讯录的时候都要释放内存,也就是销毁通讯录,以防止内存泄漏:

//销毁通讯录
void DestoryContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->size = 0;
	pc = NULL;
}

二.优化通讯录可持续读写信息

前面提到,静态版本中,当我们打开通讯录写入数据后,保存再打开时上一次写入的数据消失不见了,为解决这个问题,我们将每次输入到通讯录的信息保存的文件里,这样再打开程序的时候上次的数据仍然存在:

1.保存通讯录中的信息到文件中

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

2.加载文件信息到通讯录中

再次打开通讯录需要从文件中读数据:

//加载文件信息到通讯录
void LoadContact(Contact* pc)
{
	//读数据
	//1. 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//2. 读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//需不需要增容
			check_capacity(pc);
			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		fclose(pf);
		pf = NULL;
	}
}

三.源码

1.text.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "contatc.h"

void menu()
{
	printf("********************\n");
	printf("****1.add     2.del****\n");
	printf("****3.search  4.modify****\n");
	printf("****5.show    6.sort****\n");
	printf("****0.exit      ****\n");
	printf("********************\n");
}
enum Option
{
	EXIT,//0
	ADD, //1
	DEL, //2
	SEARCH,
	MODIFY,
	SHOW,
	SORT
};
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 SORT:
			ShowContact(&con);
			break;
		case EXIT:
			SaveContact(&con);
			DestroyContact(&con);
			printf("推出通讯录\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

2.contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

//初始化通讯录
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 DestroyContact(Contact* pc)
{
	free(pc->data);
	pc->data = NULL;
	pc->capacity = 0;
	pc->sz = 0;
	pc = NULL;
}


//检查当前通讯录的容量
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);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pc->sz].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);

	pc->sz++;
}


//显示通讯录中的信息
void ShowContact(const Contact* pc)
{
	assert(pc);
	int i = 0;
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-4d\t%-5s\t%-20s\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 (strcmp(pc->data[i].name, name) == 0)
		{
			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 (-1 == ret)
	{
		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 pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要查找的人不存在\n");
		return;
	}
	//打印信息
	printf("%-20s\t%-4s\t%-5s\t%-20s\t%-12s\n", "名字", "年龄", "性别", "地址", "电话");
	printf("%-20s\t%-4d\t%-5s\t%-20s\t%-12s\n", pc->data[pos].name,
		pc->data[pos].age,
		pc->data[pos].sex,
		pc->data[pos].addr,
		pc->data[pos].tele);
}

//修改指定联系人
void ModifyContact(Contact* pc)
{
	assert(pc);
	char name[NAME_MAX] = { 0 };
	printf("请输入要修改人的名字:>");
	scanf("%s", name);
	int pos = FindByName(pc, name);
	if (-1 == pos)
	{
		printf("要修改的人不存在\n");
		return;
	}

	printf("请输入名字:>");
	scanf("%s", pc->data[pos].name);
	printf("请输入年龄:>");
	scanf("%d", &(pc->data[pos].age));
	printf("请输入性别:>");
	scanf("%s", pc->data[pos].sex);
	printf("请输入地址:>");
	scanf("%s", pc->data[pos].addr);
	printf("请输入电话:>");
	scanf("%s", pc->data[pos].tele);

	printf("修改完成\n");
}

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

//加载文件信息到通讯录
void LoadContact(Contact* pc)
{
	//读数据
	//1. 打开文件
	FILE* pf = fopen("contact.txt", "rb");
	if (pf == NULL)
	{
		perror("LoadContact");
	}
	else
	{
		//2. 读数据
		PeoInfo tmp = { 0 };
		int i = 0;
		while (fread(&tmp, sizeof(PeoInfo), 1, pf))
		{
			//增容
			check_capacity(pc);
			pc->data[i] = tmp;
			pc->sz++;
			i++;
		}
		fclose(pf);
		pf = NULL;
	}
}

3.contact.h

#pragma once
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>

#define MAX 100
#define NAME_MAX 20
#define SEX_MAX 5
#define ADDR_MAX 30
#define TELE_MAX 12

#define DEFAULT_SZ 3
#define INC_SZ 2

//人的信息
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;

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

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

//增加联系人
void AddContact(Contact* pc);

//删除指定联系人
void DelContact(Contact* pc);

//显示通讯录中的信息
void ShowContact(const Contact* pc);

//查找指定联系人
void SearchContact(const Contact* pc);

//修改指定联系人
void ModifyContact(Contact* pc);

//保存通讯录中的信息到文件中
void SaveContact(Contact* pc);

//加载文件信息到通讯录
void LoadContact(Contact* pc);

本篇到此结束,码文不易,还请多多支持哦!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kotlin新手教程七(委托)

委托模式是软件设计模式中的一项基本技巧。在委托模式中&#xff0c;有两个对象参与处理同一个请求&#xff0c;接受请求的对象将请求委托给另一个对象来处理。kotlin中使用by实现委托。 一、类委托 类的委托实际就是一个类中定义的方法实际是调用另一个类中的对象的方法来实现…

5.7 BGP属-ORIGIN

配置BGP ORIGIN属性控制选路 1. 实验目的 熟悉BGP ORIGIN属性控制选路的应用场景掌握BGP ORIGIN属性控制选路的配置方法2. 实验拓扑 实验拓扑如图5-7所示: 图5-7:BGP ORIGIN属性控制选路 3. 实验步骤 (1)配置IP地址 …

实验室通风橱通风柜的构成

一、实验室通风橱通风柜简介通风柜是一个密闭的同时又能排风的工作空间。其设计目的是为了控制、稀释以及排除这个密闭空间内产生制造的烟气、气雾和微粒&#xff0c;同时它也是实验室预防泄露控制的重要组成部分。在大多数实验室中&#xff0c;通风柜是保护实验室操作者免受有…

ubantu python完整安装示例(python3.7.1演示)

文章目录前言准备源码包1.下载2.解压准备工作&#xff08;重要&#xff09;1.下载cmake(用于编译源码&#xff09;2.下载必要的Module注意事项编译安装链接并验证配置环境变量1.移除原3.5link2.更换默认python3 的版本为3.73.添加路径前言 为什么需要使用源码编译安装&#xf…

分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单

文章目录前言项目地址以及怎么阅读别人的代码整体代码分页数据作者是怎么处理的 usePagination顺藤摸瓜找到 api 接口的封装api 接口再往底层找全局请求封装与请求拦截器 service.ts前言 今天看一个 ts 项目的 table 模块&#xff0c;亲身体验这是公司后台管理系统一定会使用到…

Springboot @Test 给Controller接口 写 单元测试

前言 最近有小伙伴问到怎么给 controller的接口写单元测试。 单元测试是开发必不可少的一个环节。 既然有人问到了&#xff0c;那我觉得可能不止一个人不会&#xff0c;那就按照惯例&#xff0c;出手。 正文 内容&#xff1a; 主要是get 和 post 两种请求方式的接口 的 单元测…

centos7 开机自启动自定义脚本

centos7 开机自启动自定义脚本背景配置自启动jar1.首先书写自启动脚本2.在rc.local中加入脚本reboot测试docker版本的自启动背景 项目中有遇到2个问题&#xff0c; 1&#xff1a; 使用java启动jar包 2&#xff1a; docker容器中自启动个服务。 这2个都要使用linux的开机自启动问…

AcWing 1017. 怪盗基德的滑翔翼

怪盗基德是一个充满传奇色彩的怪盗&#xff0c;专门以珠宝为目标的超级盗窃犯。而他最为突出的地方&#xff0c;就是他每次都能逃脱中村警部的重重围堵&#xff0c;而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。有一天&#xff0c;怪盗基德像往常一样偷走了一颗珍贵…

【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…

使用vmware制作云平台redhat7.9镜像模板

一、概述 1.1 redhat7.9 定制镜像上传到云平台。 这个制作镜像得方式适用于多种iso 镜像。 将iso 镜像通过vmware 创建出一台虚机&#xff0c;对虚机做一些基础配置。在虚机上安装kvm 虚拟化得工具&#xff0c; 将iso 镜像在导入虚机种通过kvm创建一下虚机&#xff0c; 虚机创…

Embedding 理解

Word Embedding 单词表示最简单的是 one-hot 但是它的缺点是 矩阵表示过于稀疏&#xff0c;占用空间对相关的词语无法得知它们的含义是相近的。 Word Embedding 解决了上述两个缺点&#xff0c;一个 Word Embedding 直观的例子如下图所示。 每个维度表示一个特征&#xff0…

简述操作系统的文件系统

前言 文件系统是操作系统中负责管理持久数据的子系统&#xff0c;将用户的文件保存在硬盘等硬件设备中&#xff0c;即使断电了数据也不会丢失。 对于用户而言&#xff0c;文件是存储的最小单位&#xff0c;再少的数据也需要以文件的形式存储在外部存储器中。以硬盘为例&#…

金三银四,我不允许你们不知道这些软件测试面试题

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

DataX及DataX-Web

大数据Hadoop之——数据同步工具DataX数据采集工具-DataX datax详细介绍及使用 一、概述 DataX 是阿里云DataWorks数据集成的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、…

前端将base64图片转换成file文件

1、base64转成file具体代码 // base64图片转file的方法&#xff08;base64图片, 设置生成file的文件名&#xff09;function base64ToFile(base64, fileName) {// 将base64按照 , 进行分割 将前缀 与后续内容分隔开let data base64.split(,);// 利用正则表达式 从前缀中获取图…

PAT 甲级 1002 python 测试点1未通过

题目&#xff1a; 思路&#xff1a; 1注意多项式非零项的数目 2两项相加为0时不输出 3 测试点1未通过 代码&#xff1a; C{} Alist(input().split()) Blist(input().split())count0 for i in range(int(A[0])):C[A[i*21]]float(A[i*22])count count 1for i in range(int(B…

支付宝支付功能使用

1、进入“蚂蚁金服开放平台” https://open.alipay.com/https://open.alipay.com/ 2、下载支付宝官方 demo&#xff0c;进行配置和测试 文档地址 手机网站支付 DEMO &#xff5c; 网页&移动应用支付宝文档中心https://opendocs.alipay.com/open/02no47 demo下载 网页…

如何使用ngxin的 upstream

1.引言&#xff1a; 1.1反向代理&#xff1a; 反向代理是充当Web服务器网关的代理服务器。当您将请求发送到使用反向代理的Web服务器时&#xff0c;他们将先转到反向代理&#xff0c;由该代理将确定是将其路由到Web服务器还是将其阻止。 这意味着有了反向代理&#xff0c;您…

Java Stream流1

目录1 Stream流1.1 Stream流的生成方式1.2 Stream流中间操作之 filter1.3 Stream流中间操作之 limit 和 skip1.4 Stream流中间操作之 concat 和 distinct1 Stream流 体验Stream流需求 package test;import java.lang.reflect.Array; import java.util.ArrayList;public clas…

Arduino IDE搭建Heltec开发板开发环境

Arduino IDE搭建Heltec开发板开发环境Heltec开发板开发环境下载与搭建Arduino IDE下载与安装搭建Heltec开发板的开发环境添加package URL方法通过Git的方法安装离线安装Heltec开发板开发环境下载与搭建 Arduino IDE下载与安装 Heltec的ESP系列和大部分的LoRa系列开发板都是用A…