C语言通讯录应用程序:从设计到实现

hello,这期给大家带来C语言实现静态通讯录,主要也是建立起创建大项目的思维,与往期这两篇博客有点类似
C语言实现三子棋
C语言实现扫雷

文章目录

  • 🤓通讯录介绍
  • 😶‍🌫️效果演示
  • 🤠主题框架
    • 头文件
    • 测试文件
    • 函数实现
      • 初始化
      • 显示
      • 添加
      • 删除
      • 查找
      • 修改
      • 排序
      • 清空通讯录
  • 😵不足之处

🤓通讯录介绍

通讯录存放的是100个联系人的信息,这些信息包括

  • 姓名
  • 年龄
  • 性别
  • 电话
  • 地址

通讯录要实现的功能有

  • 添加联系人
  • 删除联系人
  • 查找联系人
  • 修改联系人的信息
  • 给通讯录排序
  • 打印通讯录
  • 销毁通讯录(清空所有联系人)

😶‍🌫️效果演示

在正式开始实现之前,我们来看一下最终的完结通讯录是怎么样的
在这里插入图片描述

🤠主题框架

大文件至少分3个文件

  • text.c->逻辑测试文件
  • comtact.c->函数定义文件
  • contact.h->函数声明,结构体定义等文件

头文件

知道了通讯录需要实现的功能后,我们就可以给出头文件

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 13
#define MAX_ADDRESS 20
typedef struct PerInfo
{
	char name[MAX_NAME];
	int age;
	char sex[MAX_SEX];
	char tele[MAX_TELE];
	char address[MAX_ADDRESS];
}PeoInfo;

typedef struct Contact
{
	PeoInfo data[MAX];
	int sz;
}Contact;

enum Opreation
{
	EXIT,
	ADD,
	DELETE,
	SEARCH,
	MODIFY,
	SHOW,
	SORT,
	DESTROY
};

void InitContact(Contact* con);
void AddContact(Contact* con);
//这里传地址节省开销
void ShowContact(const Contact* con);

void DeleteContact(Contact* con);

void SearchContact(const Contact* con);

void ModifyContact( Contact* con);

void SortContact(Contact* con);

void DestroyContact(Contact* con);

给出几点说明

  1. 通讯录是静态的,最大可以存放100个联系人的信息,所以我们需要定义变量sz表示当前有效元素的个数,所以通讯录类型应该是结构体,结构体成员是数组和有效元素个数
  2. 数组的每个元素都是联系人的信息,联系人的信息有姓名、年龄、性别、地址、电话等,所以数组元素也是一个结构体
  3. 将通讯录的功能设置为枚举变量~提高程序的可读性
  4. 数组元素定义为符号常量,方便后续统一同意更改
  5. 函数的参数都是结构体指针
    1. 这样参数传递时可以只开辟一个指针的大小
    2. 可以通过结构体指针改变结构体的内容

测试文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void menu()
{
	printf("***********Contact***********\n");
	printf("****1. add     2. delete ****\n");
	printf("****3. search  4. modify ****\n");
	printf("****5. show    6. sort   ****\n");
	printf("****7. destroy 0. exit   ****\n");
	printf("***********Contact***********\n");
}
int main()
{
	Contact con;
	InitContact(&con);
	int input = 0;
	do
	{
		menu();
		printf("请输入需要进行的操作:>");
		scanf("%d", &input);
		switch (input)
		{
		case ADD:
			AddContact(&con);

			break;
		case DELETE:
			DeleteContact(&con);
			break;
		case SEARCH:
			SearchContact(&con);
			break;
		case MODIFY:
			ModifyContact(&con);
			break;
		case SHOW:
			ShowContact(&con);
			break;
		case SORT:
			SortContact(&con);
			break;
		case DESTROY:
			DestroyContact(&con);
			break;
		case EXIT:
			break;
		default :
			break;
		}
	} while (input);
}

函数实现

初始化

由于我们申请的是一个Contact类型的变量,并没有初始化,所以我们需要将Contac变量的sz(有效联系人个数)成员初始化为0,以及整个通讯录初始化为0

//初始化
void InitContact(Contact* con)
{
	con->sz = 0;
	memset(con->data, 0, sizeof(con->data));
}

显示

我们是通过成员sz来记录该通讯录有效联系人的个数,所以打印通讯录时也只需要通过sz来判断打印结束条件(即只用打印sz个联系人的信息)

//显示
void ShowContact(const Contact* con)
{
	printf("%-10s%-10s%-10s%-20s%-20s\n", 
		"姓名", "年龄", "性别", "电话", "地址");
	for (int i = 0; i < con->sz; i++)
	{
		printf("%-10s%-10d%-10s%-20s%-20s\n",
	con->data[i].name, con->data[i].age, con->data[i].sex, con->data[i].tele, con->data[i].address);
	}
}

这里处于安全考虑,形参用const修饰

添加

添加联系人需要完成2步

  1. 输入需要添加联系人的信息(姓名、年龄、性别、电话、地址)
  2. 将通讯录的有效成员个数+1
//添加
void AddContact(Contact* con)
{
	if (con->sz == MAX)
	{
		printf("通讯录容量已经满了,无法添加\n");
		return;
	}
	printf("请输入你需要添加人的姓名:");
	scanf("%s", con->data[con->sz].name);
printf("请输出你需要添加人的年龄:");
	scanf("%d", &(con->data[con->sz].age));
	printf("请输入你需要添加人的性别:");
	scanf("%s", con->data[con->sz].sex);
	printf("请输入你需要添加人的电话:");
	scanf("%s", con->data[con->sz].tele);
	printf("请输入你需要添加人的地址:");
	scanf("%s", con->data[con->sz].address);
	con->sz++;
	printf("添加成功!\n通讯录还可以容纳%d人\n", MAX - con->sz);
}

添加的联系人存储在当前有效联系人的最后面

删除

删除需要两步

  1. 删除首先需要找到该联系人(由于后面查找功能也需要查找联系人,所以将此时的查找定义为一个函数)
  2. 用后面的联系人的信息覆盖以该联系人开始后面信息(即删除)
//查找联系人的下标
static int findContact(const Contact* con, const char* name)
{
	for (int i = 0; i < con->sz; i++)
	{
		if (strcmp(con->data[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}

//删除
void DeleteContact(Contact* con)
{
	if (con->sz == 0)
	{
		printf("当前通讯录没有联系人,无法删除\n");
		return;
	}
	char name[20];
	printf("请输出你需要删除联系人的姓名:");
	scanf("%s", name);
	//查找
	int pos = findContact(con, name);
	if (pos == -1)
	{
		printf("没有找到该联系人\n");
		return;
	}
	//删除
	memmove(&(con->data[pos]), &(con->data[pos]) + 1, 
		sizeof(con->data[0]) * (MAX - pos - 1));
	con->sz--;
	printf("删除成功!\n通讯录可容纳的人数%d\n", MAX - con->sz);
}

给出几点注意:

  • 删除可以根据用户输入的姓名找到指定的联系人进行删除(不考虑删除),也可以根据用户输入的电话进行删除
    这里实现根据用户输入的姓名进行删除
  • 删除可以将data数组后面的元素一个一个挪动到前面,我这里直接用memmove,大同小异,但是不能使用memcpy,因为内存有重复的
  • 删除后通讯录有效人数的数量-1

查找

  1. 找到该联系人的下标
  2. 打印该联系人的信息
//查找
void SearchContact(const Contact* con)
{
	printf("请输出你需要查找联系人的姓名:");
	char name[20];
	scanf("%s", name);
	int pos = findContact(con, name);
	if (pos == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
	printf("查找结果如下:\n");
	printf("%-10s%-10s%-10s%-20s%-20s\n",
		"姓名", "年龄", "性别", "电话", "地址");
	printf("%-10s%-10d%-10s%-20s%-20s\n",
		con->data[pos].name, con->data[pos].age, con->data[pos].sex,
		con->data[pos].tele, con->data[pos].address);
}

修改

当联系人不小心信息输入错了之后我们需要对它进行修改
修改分两步

  1. 找到需要修改的联系人的下标
  2. 录入修改后的信息
//修改
void ModifyContact(Contact* con)
{
	printf("请输出需要修改联系人的名字:");
	char name[20];
	scanf("%s", name);
	int pos = findContact(con, name);
	if (pos == -1)
	{
		printf("找不到该联系人\n");
		return;
	}
	printf("请输入修改后的姓名:");
	scanf("%s", con->data[pos].name);
	printf("请输入修改后的年龄:");
	scanf("%d", &(con->data[pos].age));
	printf("请输入修改后的性别:");
	scanf("%s", con->data[pos].sex);
	printf("请输入修改后的电话:");
	scanf("%s", con->data[pos].tele);
	printf("请输入修改后的地址:");
	scanf("%s", con->data[pos].address);
}

排序

排序主要为2中方式

  • 按照名字从小到大排序
  • 按照年龄从小到大排序
//自定义名字比较函数
int CmpByName(const void* e1, const void* e2)
{
	return (strcmp(((PeoInfo*)e1)->name , ((PeoInfo*)e2)->name));
}
//自定义年龄比较函数
int CmpByAge(const void* e1, const void* e2)
{
	return ((PeoInfo*)e1)->age - ((PeoInfo*)e2)->age;
}
void SortContact(Contact* con)
{
	int input = 0;
	do
	{
		printf("请选择排序方式\n1. 姓名  2.年龄\n");
		scanf("%d", &input);
		if (input == 1)
		{
			qsort(con->data, con->sz, sizeof(con->data[0]), CmpByName);
			printf("排序成功\n");
			ShowContact(con);
		}
		else if (input == 2)
		{
			qsort(con->data, con->sz, sizeof(con->data[0]), CmpByAge);
			printf("排序成功\n");
			ShowContact(con);
		}
		else printf("选择错误\n");
	} while (input != 1 && input != 2);
}

由于待排序数组是结构体数组,所以直接调用库函数qsort
注意比较函数是需要我们自己定义的

清空通讯录

比较简单,将结构体数组所有元素赋值为0,通讯录数组有效联系人置为0即可

void DestroyContact(Contact* con)
{
	printf("你确定要清空通讯录吗?(YES/NO)\n");
	char selection[MAX] = { 0 };
	scanf("%s", selection);
	fflush(stdin);
	if (strcmp(selection, "YES") != 0) return;
	memset(con->data, 0, sizeof(con->data));
	con->sz = 0;
	printf("清空成功!\n");
}

😵不足之处

此通讯录总体来说实现比较简单,类似于静态顺序表
次通讯录如下几方面不足,可以改进

  • 通讯录的大小是固定的
  • 通讯的内存存储是连续的(数组在空间中是连续存放的)
  • 通讯录的存储是存储在内存中的,没有保存在文件中,程序结束后通讯录销毁

😋😋😋本章内容到这结束了,不足之处我们后续会逐渐改进在后续文章中随着更加系统的学习我们会逐渐优化😋😋😋

完整代码在静态通讯录

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

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

相关文章

CSS 属性计算过程

CSS 属性计算过程 你是否了解 CSS 的属性计算过程呢&#xff1f; 有的同学可能会讲&#xff0c;CSS属性我倒是知道&#xff0c;例如&#xff1a; p{color : red; }上面的 CSS 代码中&#xff0c;p 是元素选择器&#xff0c;color 就是其中的一个 CSS 属性。 但是要说 CSS 属…

三十七、实战演练之接口自动化平台的文件上传

上传文件功能 上传文件功能主要针对需要测试上传文件的接口。原理是&#xff0c;把要测试上传的文件先上传到测试平台&#xff0c;然后把路径写入 用例中&#xff0c;后台真正测试时再将其进行上传。 一、上传文件模型 在testplans/models.py 模块中编写如下模型&#xff1a;…

基于深度学习方法与张量方法的图像去噪相关研究

目录 1 研究现状 1.1 基于张量分解的高光谱图像去噪 1.2 基于深度学习的图像去噪算法 1.3 基于深度学习的高光谱去噪 1.4 小结 2 基于深度学习的图像去噪算法 2.1 深度神经网络基本知识 2.2 基于深度学习的图像去噪网络 2.3 稀疏编码 2.3.1 传统稀疏编码 2.3.2 群稀…

C++习题——数组中的逆序对

剑指 Offer . 数组中的逆序对 2023/3/22美团面试 题目 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组&#xff0c;求出这个数组中的逆序对的总数。 示例2&#xff1a; 输入&#xff1a;[1&#xff0…

二分查找——我欲修仙(功法篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️我欲修仙】 学习名言&#xff1a;临渊羡鱼,不如退而结网——《汉书董仲舒传》 系列文章目录 第一章 ❤️ 二分查找 文章目录系列文章目录前言&#x1f697;&#x1f697;&#x1f697;二分查找&…

半导体器件基础08:MOS管结构和原理(2)

说在开头&#xff1a;关于海森堡和泡利&#xff08;3&#xff09; 索末菲每周都要和学生们谈话&#xff0c;跟每个学生都保持了密切联系&#xff0c;他推荐泡利和海森堡去哥廷根大学找玻恩学习&#xff0c;玻恩很赏识这两个年轻人。玻恩也有一个研讨班&#xff0c;搞了一班优秀…

在选择视觉检测设备应注意哪些误区?

目前&#xff0c;视觉检测设备已普遍成为工业生产企业改变质检方式、提高产品质量的首选。然而&#xff0c;许多企业在视觉检测设备的选择上犯了重大错误。误区一&#xff1a;检测项目模糊&#xff0c;分不清主次。检查项目不明确。对于正规品牌的视觉检测设备厂家&#xff0c;…

过拟合、验证集、交叉验证

过拟合 简单描述&#xff1a;训练集误差小&#xff0c;测试集误差大&#xff0c;模型评估指标的方差&#xff08;variance&#xff09;较大&#xff1b; 判断方式&#xff1a; 1、观察 train set 和 test set 的误差随着训练样本数量的变化曲线。 2、通过training accuracy 和…

Linux使用宝塔面板搭建网站,并内网穿透实现公网访问

文章目录前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4.固定http地址5. 配置二级子域名6.创建一个测试页面前言 宝塔面板作为简单好用的服务器运维管理面板&#xff0c;它支持Linux/Windows系统&#xff0c;我们可用它来一键配置LAMP/LNMP环境、网站、数据库、FTP等&…

线程安全(重点)

文章目录一.线程安全的概念1.1 线程安全的概念1.2 线程不安全的原因1.3 解决线程不安全二.synchronized-monitor lock(监视器锁)2.1 synchronized的特性(1)互斥(2)刷新内存(3)可重入2.2 synchronied使用方法1.直接修饰普通方法:2.修饰静态方法:3.修饰代码块:三.死锁3.1死锁的情…

Tomcat And Servlet (1)

文章目录1. Tomcat2. 下载安装3. 启动 Tomcat4. 运行 Tomcat5. Servlet5.1 创建项目5.2 引入依赖5.3 创建目录5.4 编写代码5.5 打包程序5.6 部署程序5.7 验证程序6. 安装 Smart Tomcat 插件7. 使用 SmartTomcat 插件8. 常见错误8.1 出现 4048.2 出现 4058.3 出现 5008.4 出现空…

在linux上安装配置nodejs工具,设置环境变量,设置npm国内镜像源,提高下载速度。

目录前言1&#xff0c;关于nodejs2&#xff0c;配置环境变量3&#xff0c;总结前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未经博主允许不得转载。 博主CSDN地址是&#xff1a;https://blog.csdn.net/freewebsys 博主掘金地址是&…

CSRF漏洞的概念、利用方式、防御方案

CSRF漏洞1.CSRF的概念1.1 什么是CSRF&#xff1f;1.2 基本攻击流程2.CSRF攻击实现2.1 靶场练习2.2 CSRFXSS组合拳2.2.1 攻击页面部署2.2.2 构造恶意xss语句&#xff0c;实现重复生效的CSRF3. CSRF攻击的防御**3.1 只使用JSON API****3.2 验证HTTP Referer字段****3.3 在请求地址…

卫星通信1

偏心率为0&#xff0c;则椭圆变成圆形 偏心率为1 则长轴相比短轴无限长 此时椭圆轨道变成一条直线 半焦距 ae 地球轨道面&#xff0c;称为黄道面 赤道面 中间有个夹角&#xff0c;就是23.5 一般是地心坐标系 沿椭圆轨道探测范围大 在近地点不能提供任何服务,因为覆盖面积太…

【java】笔试强训Day3【在字符串中找出连续最长的数字串与数组中出现次数超过一半的数字】

目录 ⛳选择题 1.以下代码运行输出的是 2.以下程序的输出结果为 3.下面关于构造方法的说法不正确的是 ( ) 4.在异常处理中&#xff0c;以下描述不正确的有&#xff08; &#xff09; 5.下列描述中&#xff0c;错误的是&#xff08; &#xff09; 6.…

Linux下的coredump和kdump

目录前言coredump是什么&#xff1f;运行异常代码查看本地文件多出的core文件gdb调试带上core文件kdump机制前言 在我们之前介绍进程等待的时候&#xff0c;曾经介绍过父进程会等待子进程并且回收子进程的运行结束状态&#xff08;status输出型参数&#xff09;:参考博客 当进…

【Node.js】身份认证,Cookie和Session的认证机制,express中使用session认证和JWT认证

Node.jsWeb开发模式如何选择Web开发模式身份认证什么是身份认证为什么要身份认证不同开发模式的身份认证Session认证机制提高身份认证的安全性Session的工作原理Express中使用Session认证Session认证机制的局限性JWT认证机制JWT的工作原理JWT的组成部分Express中使用JWT在登录成…

Java - 配置中心初体验

目录 前言 配置中心介绍 什么是配置中心 Nacos配置中心 数据结构 命名空间 分组 服务 配置中心添加配置 读取配置 本地添加依赖 本地添加配置 测试 结语 前言 前文讲了ELK&#xff0c;ELK说简单也简单&#xff0c;说复杂也复杂&#xff0c;但说实话&#xff0c;微…

数据库知识总结

数据库知识点总结个人向。 目录第一章 绪论第二章 关系数据库第三章 关系数据库标准语言SQL第四章 数据库安全性第五章 数据库完整性第六章 关系数据理论第七章 数据库设计第十章 数据库恢复技术第十一章 并发控制第一章 绪论 数据(data): 描述事物的符号记录。 数据库(DataB…

基于注解的Spring-AOP应用实例

1、应用场景 需求是&#xff1a;在a系统每次字典数据变更时&#xff0c;都需要给b系统同步一次数据&#xff0c;以保持两个系统字典数据相同。 字典的增、删、改、合并接口&#xff0c;都需要执行数据推送操作&#xff0c;如果不用AOP、这些接口都需要增加推送操作的代码&…