用c语言实现通讯录

目录

静态简易通讯录

代码:

功能模块展示:

设计思路:

动态简易通讯录(本质顺序表)

代码:

扩容模块展示:

设计思路:

文件版本通讯录

代码:

文件模块展示:

设计思路:


静态简易通讯录

代码:

//头文件内容

#pragma once

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

//声明一个枚举类型增加主程序代码(switch部分)可读性
enum method
{
	EXIT,
	ADD,
	DELETE,
	FIND,
	MODIFY,
	SHOW,
	DESTROY,
	SORT
};

//定义标识符常量便于对数据大小修改
#define  MAX_NAME 20
#define  MAX_SEX 6
#define  MAX_TELEPHONE 12
#define  MAX_ADDRESS 20
#define  Max_PEOINFO 1000

//定义一个联系人信息结构体类型
typedef struct PeoInfo
{
	char name[MAX_NAME];
	char sex[MAX_SEX];
	int age;
	char Telephone[MAX_TELEPHONE];
	char address[MAX_ADDRESS];
}PeoInfo;

//创建一个联系人信息结构体数组和计数联系人个数的变量组成联系人结构体
typedef struct Contact
{
	PeoInfo data[Max_PEOINFO];
	int sz;
}Contact;


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

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

//展示所有联系人函数
void ShowContact(Contact* pc);

//删除联系人函数
void DeleteContact(Contact* pc);

//查找联系人函数
void FindContact(Contact* pc);

//修改联系人函数
void ModifyContact(Contact* pc);

//清空联系人函数
void DestroyContact(Contact* pc);

//对联系人排序函数
void SortContact(Contact* pc);


//函数实现源文件内容

//初始化函数
void InitContact(Contact* pc)
{
    assert(pc);
	//利用库函数将整个数组所有元素初始化为0
	memset(pc->data, 0, sizeof(pc->data));
	//通讯录没有联系人
	pc->sz = 0;
}

//增加联系人函数
void AddContact(Contact* pc)
{
	assert(pc);
    //通讯录已满情况
	if (pc->sz == Max_PEOINFO)
	{
		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].Telephone);
	printf("请输入联系人的地址:>");
	scanf("%s", pc->data[pc->sz].address);
    //每增加一个联系人,计数变量加1
	pc->sz = pc->sz + 1;
	printf("增加联系人成功\n");
}

//为了便于局部调试程序,先实现显示函数
//显示所有联系人
void ShowContact(Contact* pc)
{
	assert(pc);
	int i = 0;
    //打印内容左对齐
	printf("%-20s\t%-6s\t%-4s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
	for (i = 0; i < pc->sz; i++)
	{
		printf("%-20s\t%-6s\t%-4d\t%-12s\t%-20s\n",
			pc->data[i].name,
			pc->data[i].sex,
			pc->data[i].age,
			pc->data[i].Telephone,
			pc->data[i].address);
	}
}

//删除联系人时我们需要知道联系人在数组中的位置(以名字删除联系人)
//名字检索函数(不需要在头文件中声明)
//此函数无法在有同名联系人时精确找到联系人
int ModifyName(char* str, Contact* pc)
{
	assert(pc && str);
	int i = 0;
	//遍历已经存储了联系人的数组元素寻找第一个名字相符的联系人并返回下标
	for (i = 0; i < pc->sz; i++)
	{
		if (0 == strcmp(str, pc->data[i].name))
			return i;
	}
	//找不到
	return -1;
}

//删除指定联系人
//此函数无法在有同名联系人时精确删除联系人
void DeleteContact(Contact* pc)
{
	assert(pc);
	//以名字查找指定联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要删除的联系人的名字\n");
	scanf("%s", name);
	int num = ModifyName(name, pc);
	//找到了
	if (num != -1)
	{
		int i = 0;
		for (i = num; i < pc->sz; i++)
		{
			memcpy(pc->data + i, pc->data + (i + 1), sizeof(PeoInfo));
		}
		//删除后计数变量减1
		pc->sz = pc->sz - 1;
		printf("删除成功\n");
	}
	//没找到
	else
		printf("删除失败,找不到指定联系人\n");
}

//查找指定联系人
//此函数无法在有同名联系人时精确查找联系人
void FindContact(Contact* pc)
{
	assert(pc);
	//以名字查找指定联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要查找的联系人的名字\n");
	scanf("%s", name);
	//调用上面定义的名字检索函数
	int num = ModifyName(name, pc);
	//找到了打印此联系人信息
	if (num != -1)
	{
		printf("%-20s\t%-6s\t%-4s\t%-12s\t%-20s\n", "姓名", "性别", "年龄", "电话", "地址");
		printf("%-20s\t%-6s\t%-4d\t%-12s\t%-20s\n",
			pc->data[num].name,
			pc->data[num].sex,
			pc->data[num].age,
			pc->data[num].Telephone,
			pc->data[num].address);
	}
	//找不到
	else
		printf("找不到指定联系人\n");
}

//修改指定联系人
void ModifyContact(Contact* pc)
{
	assert(pc);
	//以名字查找指定联系人
	char name[MAX_NAME] = { 0 };
	printf("请输入要修改的联系人的名字\n");
	scanf("%s", name);
	//调用名字检索函数
	int num = ModifyName(name, pc);
	//找到后重新输入此联系人信息
	if (num != -1)
	{
		printf("请输入新的姓名\n");
		scanf("%s", pc->data[num].name);
		printf("请输入新的性别\n");
		scanf("%s", pc->data[num].sex);
		printf("请输入新的年龄\n");
		scanf("%d", &(pc->data[num].age));
		printf("请输入新的电话\n");
		scanf("%s", pc->data[num].Telephone);
		printf("请输入新的地址\n");
		scanf("%s", pc->data[num].address);
		printf("修改联系人成功\n");
	}
	//找不到
	else
		printf("找不到指定联系人\n");
}

//清空所有联系人
void DestroyContact(Contact* pc)
{
	assert(pc);
    //调用初始化函数将联系人结构体初始化
	InitContact(pc);
	printf("联系人已清空\n");
}

//拓展功能
//以名字对所有联系人升序排序
void SortContact(Contact* pc)
{
	assert(pc);
	int i = 0;
	//选择排序
	for (i = 0; i < pc->sz -  1; i++)
	{
		int j = i;
		for (j = i; j < pc->sz; j++)
		{
			if (strcmp(pc->data[i].name, pc->data[j].name) > 0)
			{
				PeoInfo tmp;
				memcpy(&tmp, pc->data[i].name, sizeof(PeoInfo));
				memcpy(pc->data[i].name, pc->data[j].name, sizeof(PeoInfo));
				memcpy(pc->data[j].name, &tmp, sizeof(PeoInfo));

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


//主程序源文件内容

//菜单
void menu()
{
	printf("****** 1.ADD      2.DELETE ********\n");
	printf("****** 3.FIND     4.MODIFY ********\n");
	printf("****** 5.SHOW     6.DESTROY********\n");
	printf("****** 7.SORT     0.EXIT   ********\n");
}

void test()
{
	//定义一个通讯录结构体变量
	Contact con;
	//初始化
	InitContact(&con);
	enum method input = 0;
	do
	{
		menu();
		printf("请选择你的操作\n");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);
			break;
		case DELETE:
			DeleteContact(&con);
			break;
		case FIND:
			FindContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case DESTROY:
			DestroyContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case EXIT:
			printf("程序已退出\n");
			break;
		default:
			printf("输入错误,请重新输入:>\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

功能模块展示:

增加功能:

删除功能:

查找功能:

修改功能:

拓展功能:

设计思路:

要实现一个简易版静态通讯录,无非是在实现增删查改的基础上进行进一步的功能拓展,因此设计的重点是在增删查改的功能实现上,静态版本存储通讯录内容的是定长数组,因此要实现这些功能,我们需要围绕数组下标和已经存储的联系人个数作为文章。

只要知道已经存储的联系人个数,增加联系人就会变得轻而易举。而删除,查找,修改联系人时都必须先找到要操作的联系人,因此我们需要先实现一个检索联系人接口供它们调用,此接口实现的越精确,删查改就会越精确。

为了方便对数组下标进行操作,将计数联系人个数的变量和记录联系人信息的数组都作为通讯录结构体的成员,计数变量从0开始,每增加一个联系人就加1,删除减1,这样我们就会得到通讯录的实时大小从而可以非常容易的操作通讯录,实现通讯录的功能也就变的非常简单。

动态简易通讯录(本质顺序表)

代码:

动态版本的代码只需在静态版本上稍作修改即可。

//头文件增加和修改的内容

//初始容量
#define  INIT_SZ 3
//每次扩容增加容量
#define  ADD_SZ 2

//修改后通讯录结构体
typedef struct Contact
{
	//PeoInfo data[Max_PEOINFO];
    //用结构体指针代替结构体数组
	PeoInfo* data;
	int sz;
    //增加一个记录通讯录容量的变量
	int capacity;
}Contact;


//函数实现源文件增加和修改的内容

//动态版本初始化函数
void InitContact(Contact* pc)
{
	assert(pc);
    //先开辟INIT_SZ个空间
	pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
    //开辟失败
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
    //记录容量
	pc->capacity = INIT_SZ;
}

//增加联系人时需要先检查容量是否已满,若满则增加容量
//空间动态增长(无需在头文件中声明)
int mallocContact(Contact* pc)
{
	assert(pc);
	//已满
	if (pc->sz == pc->capacity)
	{
		//扩容
		PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + ADD_SZ) * sizeof(PeoInfo));
		//扩容失败
		if (ptr == NULL)
		{
			perror("mallocContact");
			return 0;
		}
		//扩容成功
		else
		{
			pc->data = ptr;
			pc->capacity += ADD_SZ;
			printf("扩容成功\n");
			return 1;
		}
	}
	//未满
	return 1;
}

//动态版本增加函数
void AddContact(Contact* pc)
{
	assert(pc);
	//扩容失败
	if (0 == mallocContact(pc))
	{
		return;
	}
	//未满或扩容成功
	printf("请输入联系人的姓名\n");
	scanf("%s", pc->data[pc->sz].name);
	printf("请输入联系人的性别\n");
	scanf("%s", pc->data[pc->sz].sex);
	printf("请输入联系人的年龄\n");
	scanf("%d", &(pc->data[pc->sz].age));
	printf("请输入联系人的电话\n");
	scanf("%s", pc->data[pc->sz].Telephone);
	printf("请输入联系人的地址\n");
	scanf("%s", pc->data[pc->sz].address);
	pc->sz = pc->sz + 1;
	printf("增加联系人成功\n");
	
}

//动态版本清空函数
void DestroyContact(Contact* pc)
{
	assert(pc);
    free(pc->data);
    pc->data = NULL;
	pc->sz = 0;
    pc->capacity = 0;
	printf("联系人已清空\n");
}

扩容模块展示:

设计思路:

静态版本的通讯录十分死板,需要存储的数据少了就会存在空间浪费,多了又会不够,因此衍生出动态版本来解决这一问题,当存储空间不够时就向内存申请新的空间来增加容量,更加灵活。

为了实现实时向内存申请空间,需要一个变量用来记录通讯录结构体的容量,当存储的联系人个数等于容量时就需要进行扩容,同时由于通讯录的大小是动态变化的,因此用结构体指针替代结构体数组来维护存储空间。

由于每次扩容后的存储空间依然是连续的,因此在没有发生访问越界时可以像操作数组一样操作指针,静态版本的大多数代码都无需修改。

文件版本通讯录

代码:

在动态版本上稍作修改。

//函数实现源文件增加和修改的内容

//容量检查函数声明
int mallocContact(Contact* pc);

//初始化时需要先读取文件contact.dat的内容到data中去
void LoadContact(Contact* pc)
{
	//打开文件
	FILE* pf = fopen("contact.dat", "rb");
    //若路径下没有此文件,则打开文件失败
	if (pf == NULL)
	{
		perror("LoadContact");
		return;
	}
	PeoInfo tmp = { 0 };
	//读文件
	while (fread(&tmp, sizeof(PeoInfo), 1, pf))
	{
		//扩容判断
		if (0 == mallocContact(pc))
			return;
		pc->data[pc->sz] = tmp;
		//每读取一个计数变量加1
		pc->sz++;
	}
	//关闭文件
	fclose(pf);
	pf = NULL;
}

//动态版本初始化函数
void InitContact(Contact* pc)
{
	assert(pc);
	pc->data = (PeoInfo*)malloc(INIT_SZ * sizeof(PeoInfo));
	if (pc->data == NULL)
	{
		perror("InitContact");
		return;
	}
	pc->sz = 0;
	pc->capacity = INIT_SZ;
	//载入文件内容
	LoadContact(pc);
}

//将通讯录信息以二进制形式保存到contact.dat文件中
void SaveContact(Contact* pc)
{
	//打开文件(若路径下没有此文件,则新建一个此文件)
	FILE* pf = fopen("contact.dat", "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);
	}
	//关闭文件
	fclose(pf);
	pf == NULL;
	printf("通讯录内容已保存到contact.dat文件中\n");
}


//主程序源文件修改内容

case EXIT:
    //在退出程序时将内存中的信息保存到文件中去
	SaveContact(&con);
	printf("程序已退出\n");
	break;

文件模块展示:

设计思路:

无论是静态版本还是动态版本,联系人信息都是存储在内存中的,一旦程序退出运行,这些信息也会随之消失,为了长久存储这些信息,衍生出了文件版本的通讯录,即利用文件操作函数将存储在内存中的信息存储到文件中长久保存。

我们只需要在动态版本的基础上增加一个读取文件函数和写入文件函数,在程序开始时先将文件中的信息读取到内存中,在程序退出时再将内存中的数据写回文件中即可。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

突破网络屏障:掌握FRP内网穿透技术

1.FRP介绍 1.frp是什么 frp 是一款高性能的反向代理应用&#xff0c;专注于内网穿透。它支持多种协议&#xff0c;包括 TCP、UDP、HTTP、HTTPS 等&#xff0c;并且具备 P2P 通信功能。使用 frp&#xff0c;您可以安全、便捷地将内网服务暴露到公网&#xff0c;通过拥有公网 I…

【C++ STL】模拟实现 string

标题&#xff1a;【C :: STL】手撕 STL _string 水墨不写bug &#xff08;图片来源于网络&#xff09; C标准模板库&#xff08;STL&#xff09;中的string是一个可变长的字符序列&#xff0c;它提供了一系列操作字符串的方法和功能。 本篇文章&#xff0c;我们将模拟实现STL的…

【机器学习】消息传递神经网络(MPNN)在分子预测领域的医学应用

1. 引言 1.1. 分子性质预测概述 分子性质预测是计算机辅助药物发现流程中至关重要的任务之一&#xff0c;它在许多下游应用如药物筛选和药物设计中发挥着核心作用&#xff1a; 1.1.1. 目的与重要性&#xff1a; 分子性质预测旨在通过分子内部信息&#xff08;如原子坐标、原…

汇总 |国内外医疗器械网络安全法规与标准

国内外关于医疗器械网络安全的法规和标准日益完善&#xff0c;旨在确保医疗器械在全生命周期内的网络安全&#xff0c;保障患者信息的安全和隐私&#xff0c;以及医疗器械的正常运行。不同国家和地区的法规和标准各有侧重&#xff0c;但都强调了医疗器械制造商、开发者、经营者…

contos7使用docker安装vulhub

contos7下使用docker安装vulhub 1. 安装docker 1. 更新yum &#xff08;1&#xff09;切换root用户 su root &#xff08;2&#xff09;更新yum yum update 2. 卸载旧版本的docker sudo yum remove docker sudo yum remove docker-client sudo yum remove docker-clien…

反AI浪潮中的新机遇:Cara艺术社区异军突起

近日,一个名为Cara的艺术社区在网络上迅速走红,其独特的反AI定位吸引了大量创意人士。在AI技术日益普及的今天,Cara社区反其道而行之,致力于打造一个无AI侵害的创作和交流环境。这一创新模式不仅赢得了艺术家的青睐,也为国内创业者提供了新的思考角度。 一、精准定位,守…

Linux shell编程基础

Shell 是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。Shell 是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问 Linux 内核的服务。 Shell 脚本&#x…

Elasticsearch中各种query的适用场景

Elasticsearch 提供了丰富的 Query 类型&#xff0c;以满足各种搜索需求。以下列举一些常见的 Query 类型&#xff0c;并分析其区别和应用场景&#xff1a; 一、 几个常用的基本Query 1. Term Query 应用场景: 查找包含特定词语的文档&#xff0c;适合精确匹配单个词语的场景…

【C++第九课 - vector】vector介绍、vector使用,vector的底层实现、杨辉三角、全排列、只出现一次的数字

目录 一、vector的介绍二、vector的使用1、vector的构造函数2、vector的插入和三种遍历方式3、开空间4、insert5、find6、erase补充 三、vector的底层实现1、成员变量2、构造函数3、push_back4、访问方式5、pop_back6、insert - pos位置插入x7、resize8、拷贝构造9、赋值10、er…

【第13章】SpringBoot实战篇之项目部署

文章目录 前言一、准备1. 引入插件2. 打包3. 启动4. 后台启动 二、跳过测试模块三、外置配置文件1.引入插件2.忽略配置文件3. 外置配置文件 总结 前言 项目部署需要把项目部署到Linux服务器上&#xff0c;SpringBoot项目通过Maven打包即可快速生成可运行Jar包程序。 一、准备 …

每日一题——Python实现PAT乙级1042 字符统计(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 优点 缺点和改进建议 时间复杂度分析 空间复杂度分析 改进后的代码 我…

【Androi】安卓发展历程详解

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

轻NAS玩客云使用Docker部署小雅并挂载到AList详细流程分享

文章目录 前言1. 本地部署AList2. AList挂载网盘3. 部署小雅alist3.1 Token获取3.2 部署小雅3.3 挂载小雅alist到AList中 4. Cpolar内网穿透安装5. 创建公网地址6. 配置固定公网地址 前言 本文主要介绍如何在安装了CasaOS的玩客云主机中部署小雅AList&#xff0c;并在AList中挂…

YOLOv8_obb的训练、验证、预测及导出[旋转目标检测实践篇]

1.旋转目标检测数据集划分和配置 从上面得到的images和labels数据还不能够直接训练,需要按照一定的比例划分训练集和验证集,并按照下面的结构来存放数据,划分代码如下所示,该部分内容和YOLOv8的训练、验证、预测及导出[目标检测实践篇]_yolov8训练测试验证-CSDN博客是重复的…

LNMP与动静态网站介绍

Nginx发展 Nginx nginx http server Nginx是俄罗斯人 Igor Sysoev(伊戈尔.塞索耶夫)开发的一款高性能的HTTP和反向代理服务器。 Nginx以高效的epoll.kqueue,eventport作为网络IO模型&#xff0c;在高并发场景下&#xff0c;Nginx能够轻松支持5w并发连接数的响应&#xff0c;并…

Redis单线程运行与CPU多核心的关系

Redis单线程运行与CPU多核心的关系 Redis作为一种高性能的内存数据库&#xff0c;以其单线程的运行模式而闻名。在高并发的场景下&#xff0c;单线程模型有助于简化开发和避免竞争条件。然而&#xff0c;随着多核CPU的普及&#xff0c;人们不禁要问&#xff0c;Redis的单线程模…

FJSP:烟花算法(FWA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

一、烟花算法介绍 参考文献&#xff1a; Tan, Y. and Y. Zhu. Fireworks Algorithm for Optimization. in Advances in Swarm Intelligence. 2010. Berlin, Heidelberg: Springer Berlin Heidelberg. 二、烟花算法求解FJSP 2.1FJSP模型介绍 柔性作业车间调度问题(Flexible …

医疗器械网络安全风险管理的基本步骤

医疗器械网络安全风险管理是一个复杂的过程&#xff0c;涉及到多个环节和步骤。以下是一些基本的步骤和关键点&#xff1a; 风险识别&#xff1a;首先需要对医疗器械的软件、网络连接和通信协议等进行漏洞分析&#xff0c;识别潜在的安全漏洞和弱点。这可能涉及对设备的渗透测…

LLVM Cpu0 新后端7 第一部分 DAG调试 dot文件 Machine Pass

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?…

Nagios的安装和使用

*实验* *nagios安装和使用* Nagios 是一个监视系统运行状态和网络信息的监视系统。Nagios 能监视所指定的本地或远程主机以及服务&#xff0c;同时提供异常通知功能等. Nagios 可运行在 Linux/Unix 平台之上&#xff0c;同时提供一个可选的基于浏览器的 WEB 界面以方便系统管…