C语言-综合案例:通讯录

传送门:C语言-第九章-加餐:文件位置指示器与二进制读写

目录

第一节:思路整理

第二节:代码编写

        2-1.通讯录初始化

        2-2.功能选择

        2-3.增加 和 扩容

        2-4.查看

        2-5.查找

        2-6.删除

        2-7.修改

        2-8.退出

第三节:测试

下期预告:


第一节:思路整理

        创建三个文件,main.c用来测试程序,Function.c用来存放函数定义,Address.h用来存放函数声明、全局变量和各种宏。

        通讯录的需求是能够长久的存储信息,这就需要用到文件操作,每次运行通讯录程序时从文件读取数据,每次关闭程序时就把数据重新写入到文件中。

        存储数据的容器:

        可以定义一个结构体类型 PeopleInfo,每有一个人就用这个类型存储它的所有信息;再定义一个结构体类型 AddressBook,它用来存储所有 PeopleInfo,并且还有通讯录的容量和已存储数量,便于管理。

Address.h
struct PeopleInfo // 一个人的信息
{char _name[20];   // 名字int _age;         // 年龄char _number[20]; // 号码
};
struct AddressBook // 通讯录 
{struct PeopleInfo* _PeoPle; // 所有人的信息int _capacity; // 通讯录的容量int _size;     // 通讯录当前存储的数量
}addressBook;

        其他问题我们在写代码的过程中一一呈现。

第二节:代码编写

        我们把通讯录的各种功能一一实现:

        2-1.通讯录初始化

        通讯录初始化包括设定通讯录的初始容量、从文件获取信息:

Address.h
#define DEL_CAPACITY 10 // 默认初始容量 
Function.c
void AddressInit()
{FILE* pf = fopen(".//AddressBook.txt","rb"); // 二进制读方式打开if (pf == NULL){perror("文件打开失败\n");return;}addressBook._capacity = DEL_CAPACITY;addressBook._PeoPle = (struct PeopleInfo*)malloc(addressBook._capacity*sizeof(struct PeopleInfo)); // 开辟空间if (addressBook._PeoPle == NULL)return;addressBook._size = 0;// 从文件中读取数据while (0 != fread(addressBook._PeoPle + addressBook._size, sizeof(struct PeopleInfo), 1, pf)){addressBook._size++;if (addressBook._size == addressBook._capacity)Expansion(); // 扩容函数,之后实现}fclose(pf);
}

        2-2.功能选择

        通讯录初始化完成后,就编写代码实现对各种功能的选择:

Function.c
void printMenu()
{printf("***********************************************\n");printf("***************1.增加*****2.删除***************\n");printf("***************3.查找*****4.修改***************\n");printf("***************5.查看*****0.退出***************\n");printf("***********************************************\n");
}void Select()
{printMenu(); // 打印菜单int select;while (1){printf("请选择:>");scanf("%d", &select);switch (select){case 1:ADD(); // 增加break;case 2:DEL(); // 删除break;case 3:Find();// 查找break;case 4:MOD(); // 修改 break;case 5:DIS(); // 查看break;case 0:EXIT();// 退出return 0;default:printf("非法的请求,请重新输入:>");}}
}

        switch 中的函数在之后一一实现。

        2-3.增加 和 扩容

Function.c
void ADD()
{struct PeopleInfo person;printf("请输入姓名:>");scanf("%s", person._name);printf("请输入年龄:>");scanf("%d", &person._age);printf("请输入号码:>");scanf("%s", person._number);// 把个人信息添加到通讯录memmove(addressBook._PeoPle+addressBook._size,&person,sizeof(person));addressBook._size++;printf("添加成功!\n"); // 提示信息if (addressBook._size == addressBook._capacity)Expansion(); // 扩容
}

        如果满了就扩容:

Address.h
#define EXP_CAPACITY 5  // 每次扩容的大小
Function.c
void Expansion()
{// 使用realloc保留原来的内容struct PeoPle* tmp = (struct PeoPle*)realloc(addressBook._PeoPle, addressBook._capacity + EXP_CAPACITY);if (tmp != NULL)addressBook._PeoPle = tmp;elseprintf("扩容失败!\n");// 更新容量addressBook._capacity = addressBook._capacity + EXP_CAPACITY;printf("扩容成功!当前容量:%d\n",addressBook._capacity); // 提示信息
}

        2-4.查看

        查看功能可以查看已经添加的联系人的所有信息:

Function.c
void DIS()
{printf("姓名			年龄			号码\n");for (int i = 0; i < addressBook._size; i++){printf("%s			%d			%s\n",addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);}
}

        2-5.查找

        查找是根据名字找到指向他信息的下标,找不到返回-1:

Function.c
int Find(const char* name)
{for (int i = 0; i < addressBook._size; i++){if (strcmp(addressBook._PeoPle[i]._name, name) == 0)return i;}printf("该联系人不存在!\n");return -1;
}

        Select 函数中的case 3的部分做如下修改: 

case 3:printf("请输入要查找的联系人:>");char name[20]; scanf("%s",name);int i = Find(name);// 查找// 打印他的信息printf("姓名			年龄			号码\n");printf("%s			%d			%s\n", addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);break;

        2-6.删除

        删除是根据名字删除指定的联系人,所以可以复用查找函数:

Function.c
void DEL()
{printf("请输入要删除的联系人:>");char name[20];scanf("%s",name);int i = Find(name);if (i >= 0){// i 位置之后先前覆盖一位,就可以删除它了for (; i < addressBook._size - 1; i++){addressBook._PeoPle[i] = addressBook._PeoPle[i + 1];}addressBook._size--;printf("删除成功!\n");}
}

        2-7.修改

        修改也是先根据名字找到相应下标,然后再修改,所以也要复用查找函数:

Fuction.c
void MOD()
{printf("请输入要修改的联系人:>");char name[20];scanf("%s", name);int i = Find(name);if (i >= 0){printf("请修改姓名:>");scanf("%s", addressBook._PeoPle[i]._name);printf("请修改年龄:>");scanf("%d", &(addressBook._PeoPle[i]._age));printf("请修改号码:>");scanf("%s", addressBook._PeoPle->_number);printf("修改成功!\n");}
}

        2-8.退出

        程序退出时需要把数据存储到文件中,然后把堆区开辟的空间释放掉:

Function.c
void EXIT()
{// 存储数据FILE* pf = fopen(".//AddressBook.txt","wb"); // 二进制写方式打开if (pf == NULL){perror("文件打开失败\n");return;}for (int i = 0; i < addressBook._size; i++){fwrite(addressBook._PeoPle+i,sizeof(struct PeopleInfo),1 , pf);}fclose(pf);// 释放堆区空间free(addressBook._PeoPle);
}

        至此代码已经编写完毕了,可以测试程序了。

第三节:测试

        先添加3个人的信息,最后输入0退出消息:

        打开文件看里面是否有内容:

        因为是二进制读写,所以看不懂;

        再次运行程序,看看文件中的信息能不能正确读取:

 

        最后测试删除、修改、查看就可以了,这里不再掩饰。

每个文件的完整代码如下:

main.c
#include "Address.h"
int main()
{AddressInit();     // 初始化Select();          // 打印菜单并选择return 0;
}

 

Address.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define DEL_CAPACITY 10 // 默认初始容量 
#define EXP_CAPACITY 5  // 每次扩容的大小
struct PeopleInfo // 一个人的信息
{char _name[20];   // 名字int _age;         // 年龄char _number[20]; // 号码
};
struct AddressBook // 通讯录 
{struct PeopleInfo* _PeoPle; // 所有人的信息int _capacity; // 通讯录的容量int _size;     // 通讯录当前存储的数量
}addressBook;void printMenu();   // 打印菜单
void Select();      // 打印菜单并选择
void AddressInit(); // 初始化通讯录
void Expansion();   // 扩容
void ADD();         // 增加
void DIS();         // 查看
int Find(const char* name); // 查找
void DEL();         // 删除
void MOD();         // 修改
void EXIT();        // 保存+释放
Function.c
#include "Address.h"
void AddressInit()
{FILE* pf = fopen(".//AddressBook.txt","rb"); // 二进制读方式打开if (pf == NULL){perror("文件打开失败\n");return;}addressBook._capacity = DEL_CAPACITY;addressBook._PeoPle = (struct PeopleInfo*)malloc(addressBook._capacity*sizeof(struct PeopleInfo)); // 开辟空间if (addressBook._PeoPle == NULL)return;addressBook._size = 0;// 从文件中读取数据while (0 != fread(addressBook._PeoPle + addressBook._size, sizeof(struct PeopleInfo), 1, pf)){addressBook._size++;if (addressBook._size == addressBook._capacity)Expansion(); // 扩容函数,之后实现}fclose(pf);
}void printMenu()
{printf("***********************************************\n");printf("***************1.增加*****2.删除***************\n");printf("***************3.查找*****4.修改***************\n");printf("***************5.查看*****0.退出***************\n");printf("***********************************************\n");
}void Select()
{printMenu(); // 打印菜单int select;while (1){printf("请选择:>");scanf("%d", &select);switch (select){case 1:ADD(); // 增加break;case 2:DEL(); // 删除break;case 3:printf("请输入要查找的联系人:>");char name[20]; scanf("%s",name);int i = Find(name);// 查找// 打印他的信息printf("姓名			年龄			号码\n");printf("%s			%d			%s\n", addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);break;case 4:MOD(); // 修改 break;case 5:DIS(); // 查看break;case 0:EXIT();// 退出return 0;default:printf("非法的请求,请重新输入:>");}}
}void ADD()
{struct PeopleInfo person;printf("请输入姓名:>");scanf("%s", person._name);printf("请输入年龄:>");scanf("%d", &person._age);printf("请输入号码:>");scanf("%s", person._number);// 把个人信息添加到通讯录memmove(addressBook._PeoPle+addressBook._size,&person,sizeof(person));addressBook._size++;printf("添加成功!\n"); // 提示信息if (addressBook._size == addressBook._capacity)Expansion(); // 扩容
}void Expansion()
{// 使用realloc保留原来的内容struct PeopleInfo* tmp = (struct PeopleInfo*)realloc(addressBook._PeoPle, addressBook._capacity + EXP_CAPACITY);if (tmp != NULL)addressBook._PeoPle = tmp;elseprintf("扩容失败!\n");// 更新容量addressBook._capacity = addressBook._capacity + EXP_CAPACITY;printf("扩容成功!当前容量:%d\n",addressBook._capacity); // 提示信息
}void DIS()
{printf("姓名			年龄			号码\n");for (int i = 0; i < addressBook._size; i++){printf("%s			%d			%s\n",addressBook._PeoPle[i]._name, addressBook._PeoPle[i]._age, addressBook._PeoPle[i]._number);}
}int Find(const char* name)
{for (int i = 0; i < addressBook._size; i++){if (strcmp(addressBook._PeoPle[i]._name, name) == 0)return i;}printf("该联系人不存在!\n");return -1;
}void DEL()
{printf("请输入要删除的联系人:>");char name[20];scanf("%s",name);int i = Find(name);if (i >= 0){// i 位置之后先前覆盖一位,就可以删除它了for (; i < addressBook._size - 1; i++){addressBook._PeoPle[i] = addressBook._PeoPle[i + 1];}addressBook._size--;printf("删除成功!\n");}
}void MOD()
{printf("请输入要修改的联系人:>");char name[20];scanf("%s", name);int i = Find(name);if (i >= 0){printf("请修改姓名:>");scanf("%s", addressBook._PeoPle[i]._name);printf("请修改年龄:>");scanf("%d", &(addressBook._PeoPle[i]._age));printf("请修改号码:>");scanf("%s", addressBook._PeoPle->_number);printf("修改成功!\n");}
}void EXIT()
{// 存储数据FILE* pf = fopen(".//AddressBook.txt","wb"); // 二进制写方式打开if (pf == NULL){perror("文件打开失败\n");return;}for (int i = 0; i < addressBook._size; i++){fwrite(addressBook._PeoPle+i,sizeof(struct PeopleInfo),1 , pf);}fclose(pf);// 释放堆区空间free(addressBook._PeoPle);
}

下期预告:

        下一次是第十章,将学习预编译相关知识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/53756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【单片机开发】单片机常用开发工具

【前言】 在嵌入式系统领域&#xff0c;单片机&#xff08;Microcontroller, MCU&#xff09;作为核心组件&#xff0c;广泛应用于智能家居、工业控制、汽车电子等众多领域。而单片机开发工具&#xff0c;则是开发者们实现创意、解决问题的重要助手。本文主要讲述目前主流的单…

港科夜闻 | 叶玉如校长出席2024科技+新质生产力高峰论坛发表专题演讲,贡献国家科技强国战略...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长出席“2024科技新质生产力高峰论坛”&#xff0c;做了题为“三个创新&#xff1a;培育和发展新质生产力、贡献国家科技强国战略”的主题演讲。该论坛于9月2日在香港召开。论坛围绕夯实基础科研、推动源头创新、…

axure判断

在auxre中我们也可以实现判断的功能&#xff0c;当目标等于什么内容时则执行下方的功能。 一、判断输入框中是否有值 画布添加一个输入框、一个文本标签删除其中内容&#xff0c;添加一个按钮&#xff0c;输入框命名为【文本显示】文本标签命名为【提示】 给按钮新增一个交互…

单向链表概述

文章目录 &#x1f34a;自我介绍&#x1f34a;单向链表概述数据域和指针域数据类型设计 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小珑也要…

(计算机网络)应用层

1.为什么需要应用层 应用层提供使用tcp&#xff0c;udp使用的方式 协议就是制定的规则 2.域名服务器概述 域名是唯一的 新增域名&#xff0c;大家都要修改这个文本文件&#xff0c;所以要进行集中管理这个文本文件&#xff0c;而不是使用本地的hosts文件 hosts文件在Windows系统…

Java | Leetcode Java题解之第397题整数替换

题目&#xff1a; 题解&#xff1a; class Solution {public int integerReplacement(int n) {int ans 0;while (n ! 1) {if (n % 2 0) {ans;n / 2;} else if (n % 4 1) {ans 2;n / 2;} else {if (n 3) {ans 2;n 1;} else {ans 2;n n / 2 1;}}}return ans;} }

部署定时任务每2天清理一次表

1、创建存储过程 create or replace procedure truct authid current_user ---使用“authid Current_user”将存储过程转化为调用者权限 as begin execute immediate truncate table rep.tmp_s_st_busi_send_arc; end; / 2、创建定时任务 begin dbms_scheduler.create…

机器学习中的聚类艺术:探索数据的隐秘之美

一 什么是聚类 聚类是一种经典的无监督学习方法&#xff0c;无监督学习的目标是通过对无标记训练样本的学习&#xff0c;发掘和揭示数据集本身潜在的结构与规律&#xff0c;即不依赖于训练数据集的类标记信息。聚类则是试图将数据集的样本划分为若干个互不相交的类簇&#xff…

【人工智能学习笔记】4_3 深度学习基础之循环神经网络

循环神经网络(Recurrent Neural Network, RNN) 是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network),循环神经网络具有短期记忆能力 RNN核心思想 RNN的结构 一个典型…

【JS逆向学习】快乐学堂登陆接口(自定义DES加密、ddddocr验证码识别)

逆向目标 网址&#xff1a;https://www.91118.com/Passport/Account/Login接口&#xff1a;https://www.91118.com/passport/Account/LoginPost参数&#xff1a; passr 逆向过程 输入手机号、密码、验证码 点击登陆&#xff0c;多试几次&#xff0c;然后观察并比较不通请求…

鸿蒙界面开发——组件(7):组件导航 页面路由

组件导航 (Navigation)(推荐) Navigation() Navigation(pathInfos: NavPathStack)Navigation是路由容器组件&#xff0c;一般作为首页的根容器&#xff0c;包括单栏(Stack)、分栏(Split)和自适应(Auto)三种显示模式。Navigation组件适用于模块内和跨模块的路由切换&#xff0c…

ApacheKafka中的设计

文章目录 1、介绍1_Kafka&MQ场景2_Kafka 架构剖析3_分区&日志4_生产者&消费者组5_核心概念总结6_顺写&mmap7_Kafka的数据存储形式 2、Kafka的数据同步机制1_高水位&#xff08;High Watermark&#xff09;2_LEO3_高水位更新机制4_副本同步机制解析5_消息丢失问…

ollama 本地部署

ollama 本地模型部署 下载安装: [link](https://ollama.com/download)下载说明 部署使用在终端查看ollama是否安装完成终端查看ollama 命令说明查看当前支持下载的模型启动对话模式默认情况下&#xff0c;ollama启动了server 的api访问功能 外部 api访问使用postman网页版本for…

【MATLAB】模拟退火算法

模拟退火算法的MATLAB实现 模拟退火算法简介模拟退火算法应用实例关于计算结果 模拟退火算法简介 1982年&#xff0c;Kirkpatrick 将退火思想引入组合优化领域&#xff0c;提出了一种能够有效解决大规模组合优化问题的算法&#xff0c;尤其对 NP 完全问题表现出显著优势。模拟…

电商平台如何实现自动监控订单签收状态,加快资金划拨进程?

资金划拨作为交易流程的核心环节之一&#xff0c;直接关系到商家资金回笼的速度、消费者购物体验的满意度以及平台自身的信誉与稳定性。 区别于自营电商&#xff0c;电商平台入驻了许多第三方商家&#xff0c;为了保障交易安全和控制风险&#xff0c;在交易未完成之前&#xff…

超声波测距模块HC-SR04(基于STM32F103C8T6HAL库)

超声波测距模块参考资料 1.电路连接及引脚配置 触发信号PA3只需要输出10us的高电平&#xff0c;所以直接设置成 普通的GPIO端口即可&#xff1b;回响信号使用外部中断&#xff0c;上升沿信号产生外部中断&#xff0c;打开定时器&#xff0c;下降沿再产生一次中断&#xff0c;读…

数据丢失?别慌!EasyRecovery带你轻松寻回!

&#x1f31f; 意外总在不经意间降临 &#x1f31f; 亲爱的小伙伴们&#xff0c;你是否有过这样的经历&#xff1a;正专心致志地处理着电脑文件&#xff0c;突然一次误操作&#xff0c;重要的资料就这样不见了&#xff0c;那种心如刀绞的感觉瞬间涌上心头。&#x1f616; 使用…

MySQL事务【后端 13】

MySQL事务 在数据库管理系统中&#xff0c;事务&#xff08;Transaction&#xff09;是一个非常重要的概念&#xff0c;它确保了数据库操作的完整性和一致性。MySQL作为一种流行的关系型数据库管理系统&#xff0c;自然也支持事务处理。本文将深入探讨MySQL事务的基本概念、特性…

StorageSync数据缓存API

uni.setStorageSyncs参数:将 data 存储在本地缓存中指定的 key 中&#xff0c;会覆盖掉原来该 key 对应的内容&#xff0c;这是一个同步接口。 uni.setStorageSync函数里面写两个参数,分别是key和值,两个参数名称可以随便取,如果有同名的key,那么后面key的值会覆盖掉前面key的值…

毫欧表设计整体思路

原因 对于焊接设备的低阻值测量&#xff0c;一般都是mΩ级别的&#xff0c;但万用表的电阻档一般都是以200Ω做为最小档位 设计原理及软件实现设计 设计思路原理图 通过串联在电路中的电流相等&#xff0c;根据阻值和电压的关系得到电阻对应大小 设计中需要考虑的问题 1…