当前位置: 首页 > 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…...

DFS(深度优先搜索)和BFS(宽度优先搜索)

目录 DFS&#xff08;深度优先搜索&#xff09; 全排列的DFS解法 利用DFS递归构建二进制串和递归树的结构剖析 DFS--剪枝 DFS例题--整数划分 BFS(宽度优先搜索) 全排列的BFS解法 DFS&#xff08;深度优先搜索&#xff09; 深度优先搜索&#xff08;Depth First Search&…...

Redis缓存穿透、击穿、雪崩问题及解决方法

系列文章目录 Spring Cache的使用–快速上手篇 分页查询–Java项目实战篇 全局异常处理–Java实战项目篇 完善登录功能–过滤器的使用 上述只是部分文章&#xff0c;对该系列文章感兴趣的可以查看我的主页哦 文章目录系列文章目录前言一、缓存穿透1.1 问题引入1.2 解决方法1.…...

HAL库 STM32 串口通信

一、实验条件将STM32的PA9复用为串口1的TX&#xff0c;PA10复用为串口1的RX。STM32芯片的输出TX和接收RX与CH340的接收RX和发送TX相连&#xff08;收发交叉且PCB上默认没有相连&#xff0c;所以需要用P3跳线帽进行手动连接&#xff09;&#xff0c;CH340的另一端通过USB口引出与…...

2023-第十四届蓝桥杯冲刺计划!

&#x1f4ac;前言 &#x1f4a1;本文以目录形式列举大纲,可根据题目点击跳转 &#x1f308;冲刺阶段目的&#xff1a;把握高频重点,结合基础算法和常考题型总结,用真题进行模拟练习 根据自己的能力熟练目前已掌握的算法&#xff0c;不会的还可以暴力 ⏳最后三个星期大家一起冲…...

内网渗透基础知识

一、内网概述 内网也指局域网&#xff0c;是指在某一区域内又多台计算机互联成的计算机组。一般是方圆几千米内&#xff0c;局域网可以实现文件管理&#xff0c;应用软件共享&#xff0c;打印机共享&#xff0c;工作组内的历程安排&#xff0c;电子邮件和传真通信服务等功能。…...

鸟哥的Linux私房菜 正则表示法与文件格式化处理

第十一章、正则表示法与文件格式化处理 https://linux.vbird.org/linux_basic/centos7/0330regularex.php 简体版 http://cn.linux.vbird.org/linux_basic/0330regularex.php 11.2.2 grep的一些高级选项 例题一、搜索特定字符串 例题二、利用中括号 [] 来搜寻集合字符 例题四…...

1630.等差子数组

1630. 等差子数组 难度中等 如果一个数列由至少两个元素组成&#xff0c;且每两个连续元素之间的差值都相同&#xff0c;那么这个序列就是 等差数列 。更正式地&#xff0c;数列 s 是等差数列&#xff0c;只需要满足&#xff1a;对于每个有效的 i &#xff0c; s[i1] - s[i] …...

CSS 属性计算过程

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

ThinkPHP02:路由

ThinkPHP02&#xff1a;路由一、路由定义二、变量规则三、路由地址四、路由参数五、路由分组六、MISS七、资源路由八、注解路由九、URL生成一、路由定义 路由默认开启&#xff0c;在 config/app.php 中可以关闭路由。 路由配置在 config/route.php 中&#xff0c;路由定义在 r…...

制作简单进销存管理系统(C#)

实验三&#xff1a;制作简单进销存管理系统 任务要求&#xff1a; 在进销存管理系统中&#xff0c;商品的库存信息有很多种类&#xff0c;比如商品型号、商品名称、商品库存量等。在面向对象编程中&#xff0c;这些商品的信息可以存储到属性中&#xff0c;然后当需要使用这些…...