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,…...
给嵌入式新手的Cortex-M0内核超详细图解:从寄存器到中断,一篇搞定STM32/GD32入门
给嵌入式新手的Cortex-M0内核超详细图解:从寄存器到中断,一篇搞定STM32/GD32入门 刚拿到STM32开发板时,看着密密麻麻的引脚和上百页的芯片手册,我完全不知道从哪里开始。直到导师指着原理图说:"把芯片想象成一个忙…...
如何快速实现Font Awesome图标字体文件格式转换:终极在线工具指南
如何快速实现Font Awesome图标字体文件格式转换:终极在线工具指南 【免费下载链接】Font-Awesome The iconic SVG, font, and CSS toolkit 项目地址: https://gitcode.com/GitHub_Trending/fo/Font-Awesome Font Awesome作为一款标志性的SVG、字体和CSS工具包…...
AI大模型入门指南:泛化、通用、涌现三大特征解析,小白也能学会收藏!
本文深入浅出地介绍了AI大模型的主要特征,包括泛化性、通用性和涌现性,并以ChatGPT为例,阐述了其如何通过巨量参数和深度网络结构展现强大的自然语言理解和生成能力。文章还详细分类并介绍了云侧大模型(如通用大模型和行业大模型&…...
YOLOv7剪枝实战:5种高效剪枝方法对比与代码实现
YOLOv7剪枝实战:5种高效剪枝方法对比与代码实现 在目标检测领域,YOLOv7以其卓越的速度-精度平衡成为工业界宠儿。但当我们将模型部署到边缘设备或需要高吞吐量的生产环境时,原始模型的计算量和参数量往往成为瓶颈。这时,模型剪枝技…...
别急着跑流程!单细胞测序数据分析前,你的GEO数据真的‘干净’吗?
别急着跑流程!单细胞测序数据分析前,你的GEO数据真的‘干净’吗? 当你在GEO数据库中兴奋地找到那个包含1534个样本的单细胞数据集时,是否曾想过——这些看似完美的数据背后可能隐藏着致命的陷阱?许多生信分析者习惯性地…...
如何通过自动化工具高效获取阴阳师游戏资源?完整实践指南
如何通过自动化工具高效获取阴阳师游戏资源?完整实践指南 【免费下载链接】OnmyojiAutoScript Onmyoji Auto Script | 阴阳师脚本 项目地址: https://gitcode.com/gh_mirrors/on/OnmyojiAutoScript 阴阳师自动化工具是一款功能强大的智能辅助应用,…...
PVB于EVA胶片的区别
PVB于EVA胶片的区别实例:PVB用于封装“双玻璃光伏组件”:玻璃+PVB+电池片+PVB+玻璃,PVB胶片已取代EVA胶片。为什么用PVB,不像我们现在一样用EVA?因为: 在玻璃…...
北京大学钟亦武老师招收博士生、实习生
点击下方卡片,关注“CVer”公众号AI/CV重磅干货,第一时间送达冲刺今年春招、秋招和实习!大家快加入2026年AI校招群!赠送今年最大的80元优惠券,大家扫码下方二维码即可加群学习!北京大学智能学院介绍&#x…...
致开发者:别再重复造轮子,这个开源商城系统让你把时间花在刀刃上
作为开发者,你是否厌倦了每次新项目都要从零搭建电商后台?商品、订单、会员、营销……这些基础模块耗费了你多少宝贵的创造力?今天,我们想和你聊聊一个能让你“拿来即用,改也不难”的解决方案——CRMEB开源商城系统。它…...
OpenCore Legacy Patcher终极指南:让你的老Mac焕发新生,体验最新macOS
OpenCore Legacy Patcher终极指南:让你的老Mac焕发新生,体验最新macOS 【免费下载链接】OpenCore-Legacy-Patcher 体验与之前一样的macOS 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否还在为老旧的Mac无法升…...
