当前位置: 首页 > news >正文

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");
}

😵不足之处

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

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

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

完整代码在静态通讯录

相关文章:

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

hello&#xff0c;这期给大家带来C语言实现静态通讯录,主要也是建立起创建大项目的思维&#xff0c;与往期这两篇博客有点类似 C语言实现三子棋 C语言实现扫雷 文章目录&#x1f913;通讯录介绍&#x1f636;‍&#x1f32b;️效果演示&#x1f920;主题框架头文件测试文件函数…...

银河麒麟v10sp2安装nginx

nginx官网下载&#xff1a;http://nginx.org/download/ 银河麒麟系统请先检查yum源是否配置&#xff0c;若没有配置请参考&#xff1a;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优雅阈值 题目内容 如果一个数组中出现次数最多的元素出现大于等于 &#xfffd;k 次&#xff0c; 被称为 &#xfffd;−优雅数组k−优雅数组 &#xff0c; &#xfffd;k 也可以被称为优雅阈值。 例如&#xff0c;数组 [1,2,3,1,2,3,…...

Win10+Anconda安装.whl文件到指定环境——以pycocotools为例

Anconda安装.whl文件到指定环境1.Whl文件2.pycocotools安装前言&#xff1a;本篇文章主要记录了两个问题&#xff1a; &#xff08;1&#xff09;Win10环境下&#xff0c;利用Anconda安装.whl文件到指定环境的方法&#xff1b; &#xff08;2&#xff09;Win10系统安装pycocoto…...

全自动托盘四向穿梭车|拥有输送系统提升机AGV的托盘四向穿梭车立体库的软硬件配置系统

托盘四向穿梭车一般是在两向穿梭车的结构上设计改进而来的&#xff0c;托盘两向穿梭车在取货时可以实现“先进先出”或“先入后出”模式&#xff0c;多用于量大且品种少的行业。但是随着市场的不断迅速发展&#xff0c;各大企业、商家不仅对于小批量、多批次的需求越来越大&…...

【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语言实现渣男通讯录

注意&#xff1a;纯属玩笑&#xff0c;博大家一乐&#xff0c;切勿当真&#x1f4d6;首先我们要知道一个渣男通讯录有哪些信息要包含哪些功能1.你的通讯录要装多少个女朋友你得规定吧&#xff1b;2.每个女朋友的姓名&#xff0c;年龄&#xff0c;电话&#xff0c;爱好这些要有吧…...

【从零开始的C语言】操作符详解

文章目录前言一、操作符分类二、算术操作符三、移位操作符3.1 左移3.2 右移四、位操作符&#xff08;重要&#xff09;五、赋值操作符六、单目操作符七、关系操作符八、逻辑操作符九、条件操作符十、逗号表达式前言 本篇文章开始&#xff0c;我将开设《从零开始的C语言》专栏&…...

黑马在线教育数仓实战1

1. 教育项目的架构说明 项目的架构: 基于cloudera manager大数据统一管理平台, 在此平台之上构建大数据相关的软件(zookeeper,HDFS,YARN,HIVE,OOZIE,SQOOP,HUE...), 除此以外, 还使用FINEBI实现数据报表展示 各个软件相关作用: zookeeper: 集群管理工具, 主要服务于…...

python中pandas模块数据处理小案例

内容目录1. 添加随机日期2. 聚合求和3.聚合求和排序4. 聚合求和排序取前十5. 聚合取极值6. 重新赋值7. 按条件赋值pandas作为数据处理的得力工具&#xff0c;简便了数据开发过程&#xff0c;之前串联了pandas的使用方法&#xff0c;现在用几个小案例巩固一下常用的pandas方法。…...

从 X 入门Pytorch——Tensor的自动微分、计算图,常见的with torch.no_grad()机制

这里写目录标题1 Pytorch计算图和自动微分2 将单个数据从计算图中剥离 .detach3 使用with torch.go_grad(): 包含的代码段不会计算微分1 Pytorch计算图和自动微分 从功能上理解&#xff1a; 计算图就是类似于数据结构中的无环有向图&#xff0c;Pytorch中的计算图就是为了记录…...

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

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

菜鸟刷题Day1

菜鸟刷题Day1 一.自守数&#xff1a;自守数_牛客题霸_牛客网 (nowcoder.com) 描述 自守数是指一个数的平方的尾数等于该数自身的自然数。例如&#xff1a;25^2 625&#xff0c;76^2 5776&#xff0c;9376^2 87909376。请求出n(包括n)以内的自守数的个数 解题思路&#x…...

cjson文件格式介绍

cjson是一种轻量级的JSON解析库&#xff0c;它支持将JSON格式的数据转换为C语言中的数据结构&#xff0c;同时也支持将C语言中的数据结构转换为JSON格式的数据。cjson的文件格式是指在使用cjson库时&#xff0c;将JSON格式的数据存储在文件中&#xff0c;然后通过cjson库读取文…...

【Nginx二】——Nginx常用命令 配置文件

Nginx常用命令 配置文件常用命令启动和重启 Nginx配置文件maineventshttp常用命令 安装完成nginx后&#xff0c;输入 nginx -&#xff1f;查询nginx命令行参数 nginx version: nginx/1.22.1 Usage: nginx [-?hvVtTq] [-s signal] [-p prefix][-e filename] [-c filename] [-…...

3月最新!AIGC公司生态地图;开发者实用ChatGPT工具清单;上手必会的SD绘图教程;字幕组全自动化流程大公开 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『光年之外诚邀产品经理加入』古典产品经理的复兴&#xff01; 光年之外创始人王慧文在社交平台发帖&#xff0c;公布联合创始人团队基…...

python - 递归函数

递归函数 什么是递归 在函数内部&#xff0c;可以调用其他函数。如果一个函数在内部调用自身本身&#xff0c;这个函数就是递归函数 递归函数必须有一个明确的结束条件每进入更深一层的递归时&#xff0c;问题规模相对于上一次递归都应减少相邻两次重复之间有紧密的联系&…...

ring_log环形日志-6M缓冲区_proc接口

文章目录log_tools.clog.cspin_lockseq_putsseq_readseq_writesingle_openmakefiletest.sh测试&#xff1a;运行./test.sh读取日志插入日志echo cat测试参考&#xff1a;log_tools.c #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #includ…...

Linux内核进程管理几种CPU调度策略

CPU调度我们知道&#xff0c;程序需要获得CPU的资源才能被调度和执行&#xff0c;那么当一个进程由于某种原因放弃CPU然后进入阻塞状态&#xff0c;下一个获得CPU资源去被调度执行的进程会是谁呢&#xff1f;下图中&#xff0c;进程1因为阻塞放弃CPU资源&#xff0c;此时&#…...

SpringBoot整合Flink(施耐德PLC物联网信息采集)

SpringBoot整合Flink&#xff08;施耐德PLC物联网信息采集&#xff09;Linux环境安装kafka前情&#xff1a;施耐德PLC设备&#xff08;TM200C16R&#xff09;设置好信息采集程序&#xff0c;连接局域网&#xff0c;SpringBoot订阅MQTT主题&#xff0c;消息转至kafka&#xff0c…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...