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);
给出几点说明
- 通讯录是静态的,最大可以存放100个联系人的信息,所以我们需要定义变量sz表示当前有效元素的个数,所以通讯录类型应该是结构体,结构体成员是数组和有效元素个数
- 数组的每个元素都是联系人的信息,联系人的信息有姓名、年龄、性别、地址、电话等,所以数组元素也是一个结构体
- 将通讯录的功能设置为枚举变量~提高程序的可读性
- 数组元素定义为符号常量,方便后续统一同意更改
- 函数的参数都是结构体指针
- 这样参数传递时可以只开辟一个指针的大小
- 可以通过结构体指针改变结构体的内容
测试文件
#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
//添加
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);
}
添加的联系人存储在当前有效联系人的最后面
删除
删除需要两步
- 删除首先需要找到该联系人(由于后面查找功能也需要查找联系人,所以将此时的查找定义为一个函数)
- 用后面的联系人的信息覆盖以该联系人开始后面信息(即删除)
//查找联系人的下标
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
查找
- 找到该联系人的下标
- 打印该联系人的信息
//查找
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);
}
修改
当联系人不小心信息输入错了之后我们需要对它进行修改
修改分两步
- 找到需要修改的联系人的下标
- 录入修改后的信息
//修改
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");
}
😵不足之处
此通讯录总体来说实现比较简单,类似于静态顺序表
次通讯录如下几方面不足,可以改进
- 通讯录的大小是固定的
- 通讯的内存存储是连续的(数组在空间中是连续存放的)
- 通讯录的存储是存储在内存中的,没有保存在文件中,程序结束后通讯录销毁
😋😋😋本章内容到这结束了,不足之处我们后续会逐渐改进在后续文章中随着更加系统的学习我们会逐渐优化😋😋😋
完整代码在静态通讯录
相关文章:
C语言通讯录应用程序:从设计到实现
hello,这期给大家带来C语言实现静态通讯录,主要也是建立起创建大项目的思维,与往期这两篇博客有点类似 C语言实现三子棋 C语言实现扫雷 文章目录🤓通讯录介绍😶🌫️效果演示🤠主题框架头文件测试文件函数…...
银河麒麟v10sp2安装nginx
nginx官网下载:http://nginx.org/download/ 银河麒麟系统请先检查yum源是否配置,若没有配置请参考:https://qdhhkj.blog.csdn.net/article/details/129680789 一、安装 1、yum安装依赖 yum install gcc gcc-c make unzip pcre pcre-devel …...
华为笔试题OD
华为笔试题OD 1题 华为od-2022.11.5-k优雅阈值 题目内容 如果一个数组中出现次数最多的元素出现大于等于 �k 次, 被称为 �−优雅数组k−优雅数组 , �k 也可以被称为优雅阈值。 例如,数组 [1,2,3,1,2,3,…...
Win10+Anconda安装.whl文件到指定环境——以pycocotools为例
Anconda安装.whl文件到指定环境1.Whl文件2.pycocotools安装前言:本篇文章主要记录了两个问题: (1)Win10环境下,利用Anconda安装.whl文件到指定环境的方法; (2)Win10系统安装pycocoto…...
全自动托盘四向穿梭车|拥有输送系统提升机AGV的托盘四向穿梭车立体库的软硬件配置系统
托盘四向穿梭车一般是在两向穿梭车的结构上设计改进而来的,托盘两向穿梭车在取货时可以实现“先进先出”或“先入后出”模式,多用于量大且品种少的行业。但是随着市场的不断迅速发展,各大企业、商家不仅对于小批量、多批次的需求越来越大&…...
【Linux】进程概念二
文章目录进程概念二1. 进程状态2. 进程状态查看3. 僵尸进程3.1 僵尸进程的危害4. 孤儿进程5. 环境变量5.1 常见环境变量5.2 查看环境变量的方法5.3 测试PATH5.4 环境变量相关的命令5.5 环境变量的组织方式5.6 通过代码获取环境变量6. 程序地址空间7. 进程地址空间8. 扩展8.1 为…...
如何用C语言实现渣男通讯录
注意:纯属玩笑,博大家一乐,切勿当真📖首先我们要知道一个渣男通讯录有哪些信息要包含哪些功能1.你的通讯录要装多少个女朋友你得规定吧;2.每个女朋友的姓名,年龄,电话,爱好这些要有吧…...
【从零开始的C语言】操作符详解
文章目录前言一、操作符分类二、算术操作符三、移位操作符3.1 左移3.2 右移四、位操作符(重要)五、赋值操作符六、单目操作符七、关系操作符八、逻辑操作符九、条件操作符十、逗号表达式前言 本篇文章开始,我将开设《从零开始的C语言》专栏&…...
黑马在线教育数仓实战1
1. 教育项目的架构说明 项目的架构: 基于cloudera manager大数据统一管理平台, 在此平台之上构建大数据相关的软件(zookeeper,HDFS,YARN,HIVE,OOZIE,SQOOP,HUE...), 除此以外, 还使用FINEBI实现数据报表展示 各个软件相关作用: zookeeper: 集群管理工具, 主要服务于…...
python中pandas模块数据处理小案例
内容目录1. 添加随机日期2. 聚合求和3.聚合求和排序4. 聚合求和排序取前十5. 聚合取极值6. 重新赋值7. 按条件赋值pandas作为数据处理的得力工具,简便了数据开发过程,之前串联了pandas的使用方法,现在用几个小案例巩固一下常用的pandas方法。…...
从 X 入门Pytorch——Tensor的自动微分、计算图,常见的with torch.no_grad()机制
这里写目录标题1 Pytorch计算图和自动微分2 将单个数据从计算图中剥离 .detach3 使用with torch.go_grad(): 包含的代码段不会计算微分1 Pytorch计算图和自动微分 从功能上理解: 计算图就是类似于数据结构中的无环有向图,Pytorch中的计算图就是为了记录…...
三十七、实战演练之接口自动化平台的文件上传
上传文件功能 上传文件功能主要针对需要测试上传文件的接口。原理是,把要测试上传的文件先上传到测试平台,然后把路径写入 用例中,后台真正测试时再将其进行上传。 一、上传文件模型 在testplans/models.py 模块中编写如下模型:…...
菜鸟刷题Day1
菜鸟刷题Day1 一.自守数:自守数_牛客题霸_牛客网 (nowcoder.com) 描述 自守数是指一个数的平方的尾数等于该数自身的自然数。例如:25^2 625,76^2 5776,9376^2 87909376。请求出n(包括n)以内的自守数的个数 解题思路&#x…...
cjson文件格式介绍
cjson是一种轻量级的JSON解析库,它支持将JSON格式的数据转换为C语言中的数据结构,同时也支持将C语言中的数据结构转换为JSON格式的数据。cjson的文件格式是指在使用cjson库时,将JSON格式的数据存储在文件中,然后通过cjson库读取文…...
【Nginx二】——Nginx常用命令 配置文件
Nginx常用命令 配置文件常用命令启动和重启 Nginx配置文件maineventshttp常用命令 安装完成nginx后,输入 nginx -?查询nginx命令行参数 nginx version: nginx/1.22.1 Usage: nginx [-?hvVtTq] [-s signal] [-p prefix][-e filename] [-c filename] [-…...
3月最新!AIGC公司生态地图;开发者实用ChatGPT工具清单;上手必会的SD绘图教程;字幕组全自动化流程大公开 | ShowMeAI日报
👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 『光年之外诚邀产品经理加入』古典产品经理的复兴! 光年之外创始人王慧文在社交平台发帖,公布联合创始人团队基…...
python - 递归函数
递归函数 什么是递归 在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数 递归函数必须有一个明确的结束条件每进入更深一层的递归时,问题规模相对于上一次递归都应减少相邻两次重复之间有紧密的联系&…...
ring_log环形日志-6M缓冲区_proc接口
文章目录log_tools.clog.cspin_lockseq_putsseq_readseq_writesingle_openmakefiletest.sh测试:运行./test.sh读取日志插入日志echo cat测试参考:log_tools.c #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #includ…...
Linux内核进程管理几种CPU调度策略
CPU调度我们知道,程序需要获得CPU的资源才能被调度和执行,那么当一个进程由于某种原因放弃CPU然后进入阻塞状态,下一个获得CPU资源去被调度执行的进程会是谁呢?下图中,进程1因为阻塞放弃CPU资源,此时&#…...
SpringBoot整合Flink(施耐德PLC物联网信息采集)
SpringBoot整合Flink(施耐德PLC物联网信息采集)Linux环境安装kafka前情:施耐德PLC设备(TM200C16R)设置好信息采集程序,连接局域网,SpringBoot订阅MQTT主题,消息转至kafka,…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
