【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);//使用完成之后释放动态申请的那些空间的函数

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

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

相关文章

2024年 前端JavaScript入门到精通 第一天 笔记

主要讲解JavaScript核心知识&#xff0c;包含最新ES6语法&#xff0c;从基础到API再到高级。让你一边学习一边练习&#xff0c;重点知识及时实践&#xff0c;同时每天安排大量作业&#xff0c;加深记忆&#xff0c;巩固学习成果。 1.1 基本软件与准备工作 1.2 JavaScript 案例 …

standalone安装部署

standalone是spark的资源调度服务&#xff1b;作用和yarn是一样的&#xff1b;standlone运行时的服务&#xff1a; master服务&#xff1b;主服务&#xff1b;管理整个资源调度&#xff1b;资源的申请需要通过master进行分配&#xff1b;类似于yarn里的ResourceManager;&#x…

v63.函数

1.初见函数 代码中有部分非常单纯/重复&#xff0c;可以将它取出来&#xff0c;制作成函数&#xff0c;而且可以反复调用。 代码复制是程序质量不良的表现。 可以使用函数优化代码&#xff0c;可以反复调用。 2.函数的定义和使用 函数整体由函数头&#xff0b;函数体组成函数…

【HarmonyOS】鸿蒙开发之prompt组件——第3.3章

prompt组件简介 prompt组件一共有三种弹出框: showToast&#xff08;&#xff09;showDialog&#xff08;&#xff09;showActionMenu&#xff08;&#xff09; 一.显示一个Toast showToast函数内参数说明如下: message&#xff1a;提示文本&#xff0c;必填项。 duratio…

【qt创建线程两种方式】

QT使用线程的两种方式 1.案例进度条 案例解析&#xff1a; 如图由组件一个进度条和三个按钮组成&#xff0c;当点击开始的时候进度条由0%到100%&#xff0c;点击暂停&#xff0c;进度条保持之前进度&#xff0c;再次点击暂停变为继续&#xff0c;点击停止按钮进度条停止。 案…

从可靠性的角度理解 tcp

可靠性是 tcp 最大的特点。常见的用户层协议&#xff0c;比如 http, ftp, ssh, telnet 均是使用的 tcp 协议。可靠性&#xff0c;即从用户的角度来看是可靠的&#xff0c;只要用户调用系统调用返回成功之后&#xff0c;tcp 协议栈保证将报文发送到对端。引起不可靠的表现主要有…

【大模型 知识图谱】ChatKBQA:KBQA知识图谱问答 + 大模型

ChatKBQA&#xff1a;KBQA知识图谱问答 大模型 提出背景传统方法处理流程ChatKBQA处理流程对比优势 总结ChatKBQA框架概览特征1&#xff1a;逻辑形式生成特征2&#xff1a;无监督实体和关系检索特征3&#xff1a;参数高效的微调特征4&#xff1a;GQoT 可解释的查询执行特征5&a…

git stash 正确用法

目录 一、背景 二、使用 2.1 使用之前&#xff0c;先简单了解下 git stash 干了什么&#xff1a; 2.2 git stash 相关命令 2.3 使用流程 1. 执行 git stash 2. 查看刚才保存的工作进度 git stash list 3. 这时候在看分支已经是干净无修改的(改动都有暂存到 stash) 4. 现在…

SpringBoot常见问题

1 引言 Spring Boot是一个基于Spring框架的快速开发脚手架&#xff0c;它简化了Spring应用的初始化和搭建过程&#xff0c;提供了众多便利的功能和特性&#xff0c;比如自动配置、嵌入式Tomcat等&#xff0c;让开发人员可以更加专注于业务逻辑的实现。   Spring Boot还提供了…

C++项目 -- 高并发内存池(五)释放内存过程

C项目 – 高并发内存池&#xff08;五&#xff09;释放内存过程 文章目录 C项目 -- 高并发内存池&#xff08;五&#xff09;释放内存过程一、Thread Cache释放内存1.完善FreeList功能2.Thread Cache释放内存 二、Central Cache释放内存三、Page Cache释放内存四、释放内存过程…

Pytorch关于CIFAR-10测试

下载 CIFAR-10数据集&#xff1a; 官网&#xff1a;https://www.cs.toronto.edu/~kriz/cifar.html CIFAR-10的网络结构&#xff1a; import torch from torch import nn from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential#定义网络结构 class Model(nn.Mo…

【社区投稿】Rust登陆华为鸿蒙操作系统之Native模块开发

Rust登陆【华为鸿蒙】操作系统之Native模块开发 名词解释 【鸿蒙操作系统】的英文全名是Open Harmony Operation System。正文将以其首字母缩写词ohos引用该词条。【鸿蒙软件开发工具包】的英文全名是Open Harmony Software Development Kit。正文也将以它的首字母缩写词ohsdk引…

C++-带你初步走进继承(1)

1.继承的概念及定义 1.1继承的概念 继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段&#xff0c;它允许程序员在 保 持原有类特性的基础上进行扩展 &#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承 呈现了面向对象 …

如何在IDEA中使用固定公网地址SSH远程连接服务器开发环境

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…

Compose 1.6 发布:性能大升级、拖放新功能、文本新变化...

翻译自&#xff1a; https://android-developers.googleblog.com/2024/01/whats-new-in-jetpack-compose-january-24-release.html 基于 1 月 24 号的 Compose 发行计划&#xff0c;我们正式推出了 Jetpack Compose 1.6 版本。 作为 Android 平台备受推崇的原生 UI 工具包&…

P4447 [AHOI2018初中组] 分组题解

题目 小可可的学校信息组总共有n个队员&#xff0c;每个人都有一个实力值。现在&#xff0c;一年一度的编程大赛就要到了&#xff0c;小可可的学校获得了若干个参赛名额&#xff0c;教练决定把学校信息组的n个队员分成若干个小组去参加这场比赛。 但是每个队员都不会愿意与实…

安全测试工具安装指南:在统信UOS上部署Burp Suite

原文链接&#xff1a;安全测试工具安装指南&#xff1a;在统信UOS上部署Burp Suite 大家好&#xff01;在网络安全领域&#xff0c;Burp Suite是一款不可或缺的工具&#xff0c;它提供了从初级映射和分析应用程序攻击面到查找和利用安全漏洞的一系列功能。今天&#xff0c;我将…

Shiro-05-shiro 基础知识补充密码学+哈希散列

密码学 密码术是隐藏或混淆数据的过程&#xff0c;因此窥探眼睛无法理解它。 Shiro的加密目标是简化JDK的加密支持并使之可用。 需要特别注意的是&#xff0c;密码通常不是特定于主题的&#xff0c;因此Shiro API的其中一个领域不是特定于主题的。 即使未使用“主题”&…

【elk查日志 elastic(kibana)】

文章目录 概要具体的使用方式一&#xff1a;查找接口调用历史二&#xff1a;查找自己的打印日志三&#xff1a;查找错误日志 概要 每次查日志&#xff0c;我都需要别人帮我&#xff0c;时间长了总觉得不好意思&#xff0c;所以这次下定决心好好的梳理一下&#xff0c;怎么查日…

spellman电源维修X3635系列CCM5P4X3635

Spellman高压发生器维修Perkin Elmer分析仪电源维修CCM5P4X3635 Perkin Elmer W102266 X射线高压发生器spellman电源维修X4297系列CT机高压电源维修CT42&#xff1b;CT70系列。SPELLMAN高压发生器应用于东芝CT机XVISION/EX、AUKLET系列、ASTEION系列、以及多排系列&#xff0c…