【进阶C语言】静态版通讯录的实现(详细讲解+全部源码)

前言
📕作者简介:热爱跑步的恒川,正在学习C/C++、Java、Python等。
📗本文收录于C语言进阶系列,本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数的介绍、自定义类型结构、动态内存管理、文件操作等,持续更新!
📘相关专栏Python,Java等正在发展,拭目以待!
📕本期内容恒川将会给大家带来实现通讯录的讲解,与上期所学的结构体内容相结合,我将会用C语言来实现通讯录的系统,并且能存放百人甚至千人的信息,每个人的信息包括:姓名、性别、年龄、电话、地址,并包含增加联系人、删除指定联系人、查找指定联系人、修改指定联系人、显示联系人信息、排序联系人等功能
📗通讯录和恒川之前写的三子棋和扫雷一样,都是多文件的形式,大家可以参考恒川之前C语言初阶系列的专栏
静态版本通讯录
- 1. 配置运行环境
- 2. 通讯录的实现
- 2.1 通讯录的功能目录
- 2.2 增加信息功能代码的实现
- 2.3 显示信息功能代码的实现
- 2.3 删除信息功能代码的实现
- 2.4 查询信息功能代码的实现
- 2.5 修改信息功能代码的实现
- 2.6 按名字排序信息功能代码的实现
- 3. 完整静态版本通讯录的全部源码
- 3.1 contact.c
- 3.2 contact.h
- 3.3 test.c
- 4. 静态版本通讯录的缺点
1. 配置运行环境
本通讯录运用到了三个文件
test.c //测试通讯录的相关功能
contact.h //声明
contact.c //通讯录的实现模块
2. 通讯录的实现
想要做通讯录,首要任务就是要把模块想好。
先打印一个通讯录的界面菜单
void menu()
{printf("********************************\n");printf("***** 1. add 2. del ***\n");printf("***** 3. search 4. modify***\n");printf("***** 5. show 6. sort ***\n");printf("***** 0. exit ***\n");printf("********************************\n");
}
2.1 通讯录的功能目录
通讯录的功能有7种:
- 增加联系人
- 删除指定联系人
- 查找联系人
- 修改联系人的信息
- 对联系人的排序
- 显示联系人的信息
- 退出通讯录
创建人的信息的结构体类型
第一步是封装一个人的信息的结构体类型
由于封装的结构体类型的名字太长,总是写的话感觉太麻烦了
对 struct PeoInfo进行了typdef类型重命名
struct PeoInfp 改成 PeoInfp
//表示一个人的信息
typedef struct PeoInfo
{char name[20];int age;char sex[5];char tele[12];char addr[30];
}PeoInfo;
以上的数值如果以后会经常用到的话,可以用#define 来定义,方便以后修改
#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30
2.2 增加信息功能代码的实现
//增加通讯录信息
void AddContact(Contact* pc)
{if (DATA_MAX == pc->sz){printf("通讯录信息存储空间已满!\n");return;}printf("请输入名字:> ");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:> ");scanf("%d", &(pc->data[pc->sz].age));printf("请输入性别:> ");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:> ");scanf("%s", pc->data[pc->sz].tele);printf("请输入住址:> ");scanf("%s", pc->data[pc->sz].addr);printf("信息添加成功!\n");pc->sz++;
}
2.3 显示信息功能代码的实现
void ShowContact(const Contact* pc)
{int i = 0;//姓名 年龄 性别 电话 地址//zhangsan 20 男 123456 北京////打印标题printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");//打印数据for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}
2.3 删除信息功能代码的实现
void DelContact(pContact pc)
{char name[MAX_NAME] = { 0 };if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}//删除//1. 找到要删除的人 - 位置(下标)printf("输入要删除人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}int i = 0;//2. 删除 - 删除pos位置上的数据for (i = pos; i<pc->sz-1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}
2.4 查询信息功能代码的实现
void SearchContact(const Contact* pc)
{char name[MAX_NAME] = {0};printf("请输入要查找人的名字:>");scanf("%s", name);//查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}//打印printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");//打印数据printf("%-10s %-4d %-5s %-12s %-30s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);
}
2.5 修改信息功能代码的实现
void ModifyContact(Contact* pc)
{char name[MAX_NAME] = {0};printf("请输入要修改人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要修改的人不存在\n");return;}//修改printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &(pc->data[pos].age));printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}
2.6 按名字排序信息功能代码的实现
//按照名字来排序
int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);printf("排序成功\n");
}
3. 完整静态版本通讯录的全部源码
3.1 contact.c
#define _CRT_SECURE_NO_WARNINGS 1#include "contact.h"void InitContact(Contact* pc)
{pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}void AddContact(Contact* pc)
{if (pc->sz == MAX){printf("通讯录已满,无法增加\n");return;}printf("请输入名字:>");scanf("%s", pc->data[pc->sz].name);printf("请输入年龄:>");scanf("%d", &(pc->data[pc->sz].age));printf("请输入性别:>");scanf("%s", pc->data[pc->sz].sex);printf("请输入电话:>");scanf("%s", pc->data[pc->sz].tele);printf("请输入地址:>");scanf("%s", pc->data[pc->sz].addr);pc->sz++;printf("添加成功\n");
}void ShowContact(const Contact* pc)
{int i = 0;//姓名 年龄 性别 电话 地址//hengchuan 20 男 123456 北京////打印标题printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");//打印数据for (i = 0; i < pc->sz; i++){printf("%-10s %-4d %-5s %-12s %-30s\n",pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr);}
}static int FindByName(const Contact* pc, char name[])
{int i = 0;for (i = 0; i < pc->sz; i++){if (0 == strcmp(pc->data[i].name, name)){return i;}}return -1;
}void DelContact(pContact pc)
{char name[MAX_NAME] = { 0 };if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}//删除//1. 找到要删除的人 - 位置(下标)printf("输入要删除人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要删除的人不存在\n");return;}int i = 0;//2. 删除 - 删除pos位置上的数据for (i = pos; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");
}void SearchContact(const Contact* pc)
{char name[MAX_NAME] = { 0 };printf("请输入要查找人的名字:>");scanf("%s", name);//查找int pos = FindByName(pc, name);if (pos == -1){printf("要查找的人不存在\n");return;}//打印printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址");//打印数据printf("%-10s %-4d %-5s %-12s %-30s\n",pc->data[pos].name,pc->data[pos].age,pc->data[pos].sex,pc->data[pos].tele,pc->data[pos].addr);
}void ModifyContact(Contact* pc)
{char name[MAX_NAME] = { 0 };printf("请输入要修改人的名字:>");scanf("%s", name);int pos = FindByName(pc, name);if (pos == -1){printf("要修改的人不存在\n");return;}//修改printf("请输入名字:>");scanf("%s", pc->data[pos].name);printf("请输入年龄:>");scanf("%d", &(pc->data[pos].age));printf("请输入性别:>");scanf("%s", pc->data[pos].sex);printf("请输入电话:>");scanf("%s", pc->data[pos].tele);printf("请输入地址:>");scanf("%s", pc->data[pos].addr);printf("修改成功\n");
}//按照名字来排序
int cmp_by_name(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}void SortContact(Contact* pc)
{qsort(pc->data, pc->sz, sizeof(PeoInfo), cmp_by_name);printf("排序成功\n");
}
3.2 contact.h
#pragma once#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>#define MAX 100
#define MAX_NAME 20
#define MAX_SEX 5
#define MAX_TELE 12
#define MAX_ADDR 30//表示一个人的信息
typedef struct PeoInfo
{char name[MAX_NAME];int age;char sex[MAX_SEX];char tele[MAX_TELE];char addr[MAX_ADDR];
}PeoInfo;//静态版本的设计
typedef struct Contact
{PeoInfo data[MAX];//存放数据int sz;//记录通讯录中的有效信息个数
}Contact, *pContact;//初始化通讯录
void InitContact(Contact* pc);//增加指定联系人
void AddContact(Contact* pc);//显示联系人信息
void ShowContact(const Contact* pc);//删除指定联系人
//void DelContact(Contact* pc);
void DelContact(pContact pc);//查找指定联系人
void SearchContact(const Contact* pc);//修改通讯录
void ModifyContact(Contact* pc);//排序通讯录元素
void SortContact(Contact* pc);
3.3 test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"void menu()
{printf("********************************\n");printf("***** 1. add 2. del ***\n");printf("***** 3. search 4. modify***\n");printf("***** 5. show 6. sort ***\n");printf("***** 0. exit ***\n");printf("********************************\n");
}enum Option
{EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT
};int main()
{int input = 0;Contact con;//通讯录//初始化通讯录//加载文件的信息到通讯录中InitContact(&con);do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case ADD:AddContact(&con);break;case DEL:DelContact(&con);break;case SEARCH:SearchContact(&con);break;case MODIFY:ModifyContact(&con);break;case SHOW:ShowContact(&con);break;case SORT:SortContact(&con);break;case EXIT:printf("退出通讯录\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
4. 静态版本通讯录的缺点
缺点:
- 通讯录的大小是固定的 - 100个元素
- 当通讯录退出后,重新运行,之前的信息都丢了
当前通讯录中的信息都是保存在内存中的,程序退出,内存就回收了,下一次重新运行程序,内存重新分配,之前的数据就不见了
所以恒川之后会给大家带来动态通讯录的版本,请大家拭目以待吧!!
如果这份博客对大家有帮助,希望各位给恒川一个免费的点赞作为鼓励,并评论收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给恒川的意见,欢迎评论区留言。
相关文章:
【进阶C语言】静态版通讯录的实现(详细讲解+全部源码)
前言 📕作者简介:热爱跑步的恒川,正在学习C/C、Java、Python等。 📗本文收录于C语言进阶系列,本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数的介绍、自定义类型结构、动态内存管理、文件操作等࿰…...
【JavaWeb】后端(Maven+SpringBoot+HTTP+Tomcat)
目录一、Maven1.什么是Maven?2.Maven的作用?3.介绍4.安装5.IDEA集成Maven6.IDEA创建Maven项目7.IDEA导入Maven项目8.依赖配置9.依赖传递10.依赖范围11.生命周期二、SpringBoot1.Spring2.SpringBoot3.SpringBootWeb快速入门二、HTTP1.HTTP-概述2.HTTP-请求协议3.HTTP-响应协议…...
面试官:准备了一些springboot相关的面试题,快来看看吧
文章目录摘要Spring Boot 中的注解 RestController 和 Controller 有什么区别?Spring Boot 中如何处理异常?使用 ExceptionHandler 注解处理特定类型的异常:使用 ExceptionHandler 注解可以将特定类型的异常映射到一个处理方法上,…...
原子的波尔模型、能量量子化、光电效应、光谱实验、量子态、角动量
一. 卢瑟福模型 1908年,卢瑟福用α粒子继续轰击金箔,发现有极少数粒子,发生了非常大的偏移。而这对于当时主流的葡萄干面包模型理论分析是相悖的。 原子可看成由带正电的原子核和围绕核运动的一些电子组成,原子中心的原子核带正…...
【如何使用Arduino控制WS2812B可单独寻址的LED】
【如何使用Arduino控制WS2812B可单独寻址的LED】 1. 概述2. WS2812B 发光二极管的工作原理3. Arduino 和 WS2812B LED 示例3.1 例 13.2 例 24. 使用 WS2812B LED 的交互式 LED 咖啡桌4.1 原理图4.2 源代码在本教程中,我们将学习如何使用 Arduino 控制可单独寻址的 RGB LED 或 …...
计算机基本知识扫盲(持续更)
计算机基本知识扫盲Q:硬盘和磁盘有什么区别?A:硬盘和磁盘都是存储数据的设备。磁盘指的是存储数据的圆形或者是方形的光盘,但是硬盘则是指机械式硬盘和固态硬盘。磁盘一般用于存储少量数据,例如软件安装文件、音乐和电…...
学习大数据需要什么语言基础
Python易学,人人都可以掌握,如果零基础入门数据开发行业的小伙伴,可以从Python语言入手。 Python语言简单易懂,适合零基础入门,在编程语言排名上升最快,能完成数据挖掘、机器学习、实时计算在内的各种大数…...
ElasticSearch——详细看看ES集群的启动流程
参考:一起看看ES集群的启动流程 本文主要从流程上介绍整个集群是如何启动的,集群状态如何从Red变成Green,然后分析其他模块的流程。 这里的集群启动过程指集群完全重启时的启动过程,期间要经历选举主节点、主分片、数据恢复等重…...
【教学类-30-01】5以内加法题不重复(一页两份)(包含1以内、2以内、3以内、4以内、5以内加法,抽取最大不重复数量)
作品样式: 背景需求: 虽然学前阶段就对幼儿训练加减法列式题遭到诟病,但是从不少幼儿(特别是二胎)在家中已经开始适应加减法题型了。 结合中班年龄特点,我从5以内的不重复加法题开始实验(雪花…...
写博客8年与人生第一个502万
题记:我们并非生来强大,但依然可以不负青春。 原本想好好写一下如何制定一个目标并通过一点一滴的努力去实现,这三年反思发现其实写自己的经历并不重要。 很多人都听过一句话:榜样的力量是无穷的。 更现实和实际的情况是&#x…...
【华为OD机试真题】日志采集系统(javapython)
日志采集系统 时间限制:1s空间限制:256MB限定语言:不限 题目描述: 日志采集是运维系统的的核心组件。日志是按行生成,每行记做一条,由采集系统分 批上报。 如果上报太频繁,会对服务端造成压力;如果上报太晚,会降低用户的体验;如果一 次上报的条数太多,会导致超时…...
epoll源码剖析
文章目录1.前言2.应用层的体现3.两个重要结构(1)eventpoll(2)epitem4.四个函数(1)epoll_create源码(2)epoll_ctl源码(3)epoll_wait的源码(4)epoll_event_callback()5.水平触发和边缘触发1.状态变化2.LT模式3.ET模式1.前言 好久好久没有更新博客了,最近一直在实习&a…...
Linux驱动开发——高级I/O操作(一)
一个设备除了能通过读写操作来收发数据或返回、保存数据,还应该有很多其他的操作。比如一个串口设备还应该具备波特率获取和设置、帧格式获取和设置的操作;一个LED设备甚至不应该有读写操作,而应该具备点灯和灭灯的操作。硬件设备是如此众多,…...
适配器模式:C++设计模式中的瑞士军刀
适配器模式揭秘:C设计模式中的瑞士军刀引言设计模式的重要性适配器模式简介与应用场景适配器模式在现代软件设计中的地位与价值适配器模式基本概念适配器模式的定义与核心思想类适配器与对象适配器的比较设计原则与适配器模式的关系类适配器实现类适配器模式的UML图…...
【三十天精通Vue 3】 第三天 Vue 3的组件详解
✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: 三十天精通 Vue 3 文章目录引言一、Vue 3 组件的概述1. Vue 3 的组件系统2. Vue 3 组件的特点…...
SqlServer实用系统视图,你了解多少?
SqlServer实用系统视图,你了解多少?前言master..spt_valuessysdatabasessysprocesses一套组合拳sysobjectssys.all_objectssyscolumnssystypessyscommentssysindexes结束语前言 在使用任何数据库软件的时候,该软件都会提供一些可能不是那么公…...
NodeJS Cluster模块基础教程
Cluster简介 默认情况下,Node.js不会利用所有的CPU,即使机器有多个CPU。一旦这个进程崩掉,那么整个 web 服务就崩掉了。 应用部署到多核服务器时,为了充分利用多核 CPU 资源一般启动多个 NodeJS 进程提供服务,这时就…...
[C++笔记]vector
vector vector的说明文档 vector是表示可变大小数组的序列容器(动态顺序表)。就像数组一样,vector也采用连续的存储空间来储存元素。这就意味着可以用下标对vector的元素进行访问,和数组一样高效。与数组不同的是,它的大小可以动态改变——…...
Python 迁移学习实用指南:1~5
原文:Hands-On Transfer Learning with Python 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象,只关心如…...
【CSS重点知识】属性计算的过程
✍️ 作者简介: 前端新手学习中。 💂 作者主页: 作者主页查看更多前端教学 🎓 专栏分享:css重难点教学 Node.js教学 从头开始学习 ajax学习 标题什么是计算机属性确定声明值层叠冲突继承使用默认值总结什么是计算机属性 CSS属性值的计算…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
