【C语言】通讯录(静态版本+动态版本)思路解析+完整源代码
通讯录
由于代码比较长,为了增加可读性,分成了contact.h,contact.c,test.c,分别用来声明函数或者类型,实现函数功能,测试代码
contact.h


我们希望通讯录具有增加联系人,删除联系人,显示联系人,找查联系人,修改联系人,排序的功能,联系人的信息具有名字,年龄,性别,电话,地址的信息
由于每次对通讯录进行操作都要把data数组和存的联系人的个数sz传过去,那干脆把他们两个打包到一个结构体类型struct contact里面
枚举类型是为了增加test.c里面switch语句选项的可读性,默认第一个成员也就是EXIT就是0,那么我们就可以把switch语句里面的0换成EXIT了。
test.c

contact.c

首先把所有data数组的所有内容以及p->sz初始化成0
增加联系人的函数

一次性增加一个联系人的信息

数字表示打印多少位,负号表示左对齐,\t表示插入一个制表符,能够让每一行对齐

在查找联系人,删除联系人,修改联系人信息的函数中,我们都需要先找到某个联系人,为了避免写三份类似的代码,我们这里使用了一个函数。

查找联系人的函数

修改信息的函数

排序的函数

像这样静态版本的通讯录问题还是比较大的,首先就是我们不管存多少个联系人的信息,上来都创建了一个100个元素的数组,这样对内存的开销就比较大,如果我们存的信息少,就浪费了内存,如果存的太多,又要去修改最大容量,在我们学习了动态内存开辟之后,我们可以对通讯录进行如下修改:通讯录刚上来可以存放三个联系人的信息,当通讯录满了之后,自动扩充两个人的名额。于是我们就可以把data数组改成一个指针,为了尽可能少的改动原来的代码,我们把这个指针的名字也叫做data。这与原来数组名的含义都是地址,现在data是一个people*类型的指针,我们可以把使用malloc,realloc函数开辟的内存首地址存到里面去。
那么我们先对以前的contact类型进行以下修改

实际上需要修改的函数只有初始化通讯录的函数和增加联系人的函数。

首先使用malloc开辟一块能存放三个people类型变量的空间,并返回首地址存到data里面去。这里申请了空间由于后面要用,所以并没有及时释放掉,最后关闭通讯录的时候再释放即可。
然后是增加联系人的函数,在增加之前我们先要判断一下通讯录是不是已经满了,我们使用下面的函数来实现这个功能

由于realloc增容可能会失败,因此必须检查,在增容完毕之后要应该更改掉当前的最大容量capacity。
然后在添加联系人的函数中调用这个检查容量的函数

由于以上所有动态内存开辟的空间都没有被释放掉,因此我们在推出这个通讯录的时候应该及时释放掉,在test函数这里调用一个用来释放内存的函数

这个函数也是需要我们自己编写的

这样我们的通讯录就是动态版本的了。
源代码
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
void menu() {printf("******1.add*****2.del***********\n");printf("******3.search**4.modify********\n");printf("******5.show****6.sort**********\n");printf("***********0.exit***************\n");
}
void test() {int input = 0;//创建通讯录contact con = {0};do {menu();printf("请选择功能\n");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:release(&con);printf("退出通讯录\n");break;default:printf("输入错误,请重新选择\n");}} while (input);
}
int main() {test();return 0;
}
contact.c
#define _CRT_SECURE_NO_WARNINGS
#include"contact.h"
//初始化通讯录的函数
void initcontact(contact* p) {p->data = (people*)malloc(DEFAULT_NUM *sizeof(people));//刚上来容量是3if (p->data == NULL) {perror("initcontact");return;}p->sz = 0;p->capacity = DEFAULT_NUM;
}
int check_capacity(contact*p) {if (p->capacity == DEFAULT_NUM) {//扩大容量people* ptr=(people*)realloc(p->data, (DEFAULT_NUM + INC) * sizeof(people));//一个变量名为ptr的people*类型指针,用来接收调整之后的空间首地址if (ptr == NULL) {perror(check_capacity);return 0;}else {p->data = ptr;//这个ptr可能是原来的data,也可能不是,增容成功之后赋给datap->capacity += INC;return 1;}}return 1;//压根不需要增容,也返回1
}
//添加联系人的函数,动态版本
void addcontact(contact* p) {int ret = check_capacity(p);if (0 == ret) {return;}printf("请输入名字\n");scanf("%s", p->data[p->sz].name);printf("请输入年龄\n");scanf("%d", &(p->data[p->sz].age));printf("请输入性别\n");scanf("%s", p->data[p->sz].sex);printf("请输入电话号码\n");scanf("%s", p->data[p->sz].tele);printf("请输入地址\n");scanf("%s", p->data[p->sz].address);p->sz++;printf("增加联系人成功\n");
}
//显示联系人的函数
void showcontact(const contact* p) {int i = 0;for (i = 0; i < p->sz; i++) {//打印标题printf("%-10s\t,%-4s\t,%-5s\t,%-12s\t,%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-10s\t,%-4d\t,%-5s\t,%-12s\t,%-30s\n",p->data[i].name,p->data[i].age,p->data[i].sex,p->data[i].tele,p->data[i].address);}
}
//通过名字找查联系人的函数
// 由于在delcontact,searchcontact,modifycontact函数中都需要找查
// 为了避免写三份类似的代码,我们使用函数来实现找查联系人的功能
int find_by_name(contact* p, char name[]) {//找查联系人int i = 0;for (i = 0; i < p->sz; i++) {if (strcmp(p->data[i].name, name) == 0) {return i;//找到了,返回下标}}return -1;//没有找到,返回-1
}
//删除联系人的函数(暂时不考虑两个人的名字相同的情况)
void delcontact(contact* p) {char name[20] = { 0 };printf("请输入要删除的人名字\n");scanf("%s", name);int i = 0;int del = 0;//记录要删除的人信息在data数组中的下标int flag = 0;if (p->sz == 0) {printf("通讯录为空,无法删除\n");}//找查联系人del = find_by_name(p, name);//删除联系人if (del == -1) {printf("要删除的人不存在\n");return;}for (i = del; i < p->sz - 1; i++) {p->data[i] = p->data[i + 1];//循环用后面的元素覆盖前面的元素}p->sz--;//如果要删除最后一个联系人,也就是下标为sz-1的那个人的信息//由于刚上来del就是sz-1,不会进入循环,也就不会覆盖掉最后一个元素//但是sz--了,就访问不到最后一个元素了,效果上就好像删除了最后一个联系人printf("删除成功\n");
}
//查找某个联系人的函数
void searchcontact(contact* p) {char name[20] = { 0 };printf("请输入要查找的人的名字\n");scanf("%s", name);int pos = find_by_name(p, name);//返回的就是要找的这个人的信息在data数组中的下标 if (pos == -1) {printf("查无此人\n");return;}else {//打印这一个人的信息printf("%-10s\t,%-4s\t,%-5s\t,%-12s\t,%-30s\n", "名字", "年龄", "性别", "电话", "地址");printf("%-10s\t,%-4d\t,%-5s\t,%-12s\t,%-30s\n",p->data[pos].name,p->data[pos].age,p->data[pos].sex,p->data[pos].tele,p->data[pos].address);}
}
//修改信息的函数
void modifycontact(contact* p) {char name[20] = { 0 };printf("请输入要修改信息的人的名字\n");scanf("%s", name);int pos = find_by_name(p, name);if (pos == -1) {printf("查无此人\n");}else {printf("请输入名字\n");scanf("%s", p->data[pos].name);printf("请输入年龄\n");scanf("%d", &(p->data[pos].age));printf("请输入性别\n");scanf("%s", p->data[pos].sex);printf("请输入电话号码\n");scanf("%s", p->data[pos].tele);printf("请输入地址\n");scanf("%s", p->data[pos].address);printf("修改联系人信息成功\n");}
}
//qsort需要的比较大小的函数
int cmp_by_name(void* str1,void* str2) {return strcmp(((people*)str1)->name, ((people*)str2)->name);
}
//根据名字对信息排序的函数
void sortcontact(contact* p) {qsort(p->data, p->sz, sizeof(p->data[0]), cmp_by_name);printf("排序成功\n");
}
void release(contact* p) {free(p->data);p->data = NULL;//注意是data指向的空间被释放掉,不是p指向的空间被释放掉p->capacity = 0;p->sz = 0;
}
contact.h
#pragma once
//用于各种函数或者类型的声明
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 100 //通讯录能添加的最大人数
#define DEFAULT_NUM 3//刚上来的默认容量是3
#define INC 2//到达最大容量的时候一次性扩充的个数
typedef struct people {char name[20];int age;char sex[5];char tele[12];char address[30];
}people;//创建结构体类型并重命名为people
typedef struct contact {people* data;//一个名为data的指针变量int sz;//用来记录通讯录里面存了几个人的信息了int capacity;//记录当前最大容量
}contact;
//枚举类型,增加可读性,默认EXIT就是0
enum OPTION {EXIT,ADD,DEL,SEARCH,MODIFY,SHOW,SORT,
};
void initcontact(contact* p);//初始化通讯录的函数,把通讯录中内容初始化为0
void addcontact(contact* p);//增加联系人的函数
void showcontact(const contact* p);//显示所有联系人的函数,显示并不会修改p指向的内容,因此加了const
void delcontact(contact* p);//删除联系人的函数
void searchcontact(contact* p);//找查某个联系人的函数
void modifycontact(contact* p);//修改信息的函数
void sortcontact(contact* p);//根据名字对信息排序的函数
void release(contact* p);//使用完成之后释放动态申请的那些空间的函数
相关文章:
【C语言】通讯录(静态版本+动态版本)思路解析+完整源代码
通讯录 由于代码比较长,为了增加可读性,分成了contact.h,contact.c,test.c,分别用来声明函数或者类型,实现函数功能,测试代码 contact.h 我们希望通讯录具有增加联系人,删除联系人…...
spring boot自动装配及自动装配条件判断
第一步需要在pom.xml文件指定需要导入的坐标 要是没有自动提示需要检查maven有没有 实现代码 /*springboot第三方自动配置实现方法 * 什么是自动配置 自动配置就是springboot启动自动加载的类不需要在手动的控制反转自动的加入bean中 * * *//*第一种方案包扫描 不推荐因为繁琐…...
LeetCode--2298. 周末任务计数
文章目录 1 题目描述2 测试用例3 解题思路 1 题目描述 表: Tasks ------------------- | Column Name | Type | ------------------- | task_id | int | | assignee_id | int | | submit_date | date | -------------------task_id 是该表的主键(具有唯一值…...
从零开始学习Netty - 学习笔记 - NIO基础 - ByteBuffer: 简介和基本操作
NIO基础 1.三大组件 1.1. Channel & Buffer Channel 在Java NIO(New I/O)中,“Channel”(通道)是一个重要的概念,用于在非阻塞I/O操作中进行数据的传输。Java NIO提供了一种更为灵活和高效的I/O处理方…...
Chatgpt润色文章“咒语”
文章目录 前言一、汉译英二、语法校正三、润色英文段落结构和句子逻辑 前言 一些Chatgpt润色文章常用的命令。 一、汉译英 I am a researcher studying Aerospace Manufacturing and now trying to revise my manuscript which will be submitted to the journal of Nature.I…...
【OpenGL教程2】 简单案例介绍Python 中的 OpenGL
目录 一、介绍二、安装三、编码练习四、结论 一、介绍 在本教程中,我们将学习如何在 Python 中使用PyOpenGL库。OpenGL是一个图形库,受Windows、Linux和MacOS等多个平台支持,也可用于多种其他语言;然而,这篇文章的范围…...
评估方法:CMMI/能力成熟度模型集成
一、什么是CMMI CMMI,全称为Capability Maturity Model Integration,即能力成熟度模型集成。它是由美国卡内基梅隆大学软件工程研究所研发的过程改进模型,也是国际上用于评价软件企业能力成熟度的一项重要标准。 CMMI的目的是帮助软件企业对…...
Gin框架: HTML模板渲染之配置与语法详解
Gin的HTML模板配置 1 )单一目录的配置 配置模板目录,在与main.go同级下, 新建目录,下面二选一,仅作举例, 这里选择 tpls templatestpls 在 tpls 目录下新建 news.html <!-- 最简单的 --> <h1>News Page</h1>&l…...
.NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
一、效果 记录日志为文档 记录日志到数据库 二、添加NuGet包 三、log4net.config代码配置 <?xml version"1.0" encoding"utf-8" ?> <log4net><!-- Debug日志 --><appender name"RollingFileDebug" type"log4net…...
Day36 贪心算法 part05
划分字母区间 一个字母区间仅有几个字母前一个字母区间有的字母后面都没有 合并区间 天才举一反三写出来了...
C#计算矩形面积:通过定义结构 vs 通过继承类
目录 一、涉及到的知识点 1.结构 2.结构和类的区别 3.继承 4.使用类继承提高程序的开发效率 5.属性 (1)属性定义 (2)get访问器 (3)set访问器 6. 属性和字段的区别 二、实例:通过定义…...
【复现】Panalog大数据日志审计系统 RCE漏洞_51
目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一: 四.修复建议: 五. 搜索语法: 六.免责声明 一.概述 Panalog大数据日志审计系统定位于将大数据产品应用于高校、 公安、 政企、 医疗、 金融、 能源等行业之中,针对网络流…...
react【五】redux/reduxToolkit/手写connext
文章目录 1、回顾纯函数2、redux2.1 redux的基本使用2.2 通过action修改store的数值2.3 订阅state的变化2.4 目录结构2.5 Redux的使用过程2.6 redux的三大原则2.7 Redux官方图 3、redux在React中的使用4、react-redux使用4.1 react-redux的基本使用4.2 异步请求 redux-thunk4.3…...
.NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2
前言 很多同学都不愿给电脑设动态壁纸,其中有个重要原因就是嫌它占资源过多。今天大姚分享一个.NET开源、免费(MIT license)的一个小而快并且功能强大的 Windows 动态桌面软件,支持视频和网页动画播放:DreamScene2。 …...
jsp计算机线上教学系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
一、源码特点 JSP 计算机线上教学系统是一套完善的java web信息管理系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql5…...
RabbitMQ的高可用机制
RabbitMQ通过多种机制提供高可用性(HA)支持,以确保消息系统的稳定性和可靠性。下面将详细介绍这些机制,并提供代码示例。 集群(Clustering) RabbitMQ的集群提供了高可用性和负载均衡。集群中的节点共享同一个Erlang分布式数据库…...
人机协同中的贝叶斯和马尔可夫
人机协同中的马尔可夫链是指在人与机器之间协同工作过程中,可能涉及到的状态转移概率模型。马尔可夫链是一种数学模型,描述了在给定当前状态下,未来状态的概率分布只依赖于当前状态,而与过去状态无关的随机过程。在人机协同工作中…...
STM32的SDIO
一.SDIO简介 SDIO,全称Secure Digital Input/Output,是一种用于在移动设备和嵌入式系统中实现输入/输出功能的接口标准。它结合了SD卡的存储功能和I/O功能,允许设备通过SD卡槽进行数据输入输出和外围设备连接。 SDIO接口通常被用于连接各种…...
Unity中的Lerp插值的使用
Unity中的Lerp插值使用 前言Lerp是什么如何使用Lerp 前言 平时在做项目中插值的使用避免不了,之前一直在插值中使用存在误区,在这里浅浅记录一下。之前看的博客或者教程还多都存在一个“永远到达不了,只能无限接近”的一个概念。可能是之前脑…...
年后上来面了一个来字节要求月薪23K,明显感觉他背了很多面试题...
最近有朋友去字节面试,面试前后进行了20天左右,包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说,80%的人都会栽在第一轮面试,要不是他面试前做足准备,估计都坚持不完后面几轮面试。 其实&…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
