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,…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
yaml读取写入常见错误 (‘cannot represent an object‘, 117)
错误一:yaml.representer.RepresenterError: (‘cannot represent an object’, 117) 出现这个问题一直没找到原因,后面把yaml.safe_dump直接替换成yaml.dump,确实能保存,但出现乱码: 放弃yaml.dump,又切…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...
