探索C语言数据结构:利用顺序表完成通讯录的实现

embedded/2024/10/21 7:41:35/

在好久之前我就已经学习过顺序表,但是在前几天再次温习顺序表的时候,我惊奇的发现顺序编表可以完成我们日常使用的通讯录的功能,那么今天就来好好通过博客总结一下通讯录如何完成吧。

常常会回顾努力的自己,所以要给自己的努力留下足迹。

为今天努力的自己打个卡,留个痕迹吧

                                                                                                        2024.04.20     小闭


顺序表

顺序表在好久之前就已经写过一篇博客了,如果没了解过顺序表的佬,可以先去了解一下唔。传送门:http://t.csdnimg.cn/DhcZ7icon-default.png?t=N7T8http://t.csdnimg.cn/DhcZ7

了解过顺序表的就可以跟着往下看看如何使用顺序表来实现通讯录,下面先给大家展示一下顺表的C语言代码,不然等下就对通讯录的一些知识点给搞迷糊了。

顺序表是我们对数据进行管理的一种简单结构,他的底层就是数组,这里我们对数组进行封装就形成了我们的通讯录。

顺序表的头文件 "SL.h"

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<assert.h>
#include"Contact.h"typedef struct Contact Datetype;struct SEQlist
{Datetype*  a;int size;int capacity;};typedef struct SEQlist SL;
void Init(SL* s);
void Print(SL* s);
void pushFront(SL* s, Datetype x);void INti(SL* s);
void tCheckCapacity(SL* s);
void SeqListPushBack(SL* s, Datetype x);
void SeqListPopBack(SL* s);
void SeqListPushFront(SL* s, Datetype x);
void SeqListPopFront(SL s);
void SeqListDestory(SL* s);
void SeqListinsert(SL* s, int pos,Datetype x);
void SeqListdel(SL* s, int pos);

顺序表的“.c”文件SL.c 

#define _CRT_SECURE_NO_WARNINGS 1
#include "SL.h"//void Print(SL* s)
//{
//	for (int i = 0; i < s->size; i++)
//	{
//		printf("%d ", s->a[i]);
//	}
//	printf("\n");
//}void Init(SL* s)
{s->a = NULL;s->size = 0;         // 表示数组中存储了多少个数据s->capacity = 0;   数组实际能存数据的空间容量是多大 }void CheckCapacity(SL* s)
{// 如果没有空间或者空间不足,那么我们就扩容if (s->size == s->capacity){int newcapacity = s->capacity == 0 ? 4 : s->capacity * 2;Datetype* tmp = (Datetype*)realloc(s->a, newcapacity * sizeof(Datetype));if (tmp == NULL){printf("realloc 失败\n");exit(-1);}s->a = tmp;s->capacity = newcapacity;}
}void SeqListPushBack(SL* s, Datetype x)
{CheckCapacity(s);s->a[s->size] = x;s->size++;
}void SeqListPopBack(SL* s)
{// 暴力处理方式assert(s->size > 0);s->size--;
}void SeqListPushFront(SL* s, Datetype x)
{CheckCapacity(s);// 挪动数据int end = s->size - 1;while (end >= 0){s->a[end + 1] = s->a[end];--end;}s->a[0] = x;s->size++;
}void SeqListPopFront(SL* s)
{assert(s->size > 0);// 挪动数据int begin = 1;while (begin < s->size){s->a[begin - 1] = s->a[begin];++begin;}s->size--;
}void SeqListDestory(SL* s)
{free(s->a);s->a = NULL;s->capacity = s->size = 0;
}void SeqListinsert(SL* s,int pos, Datetype x)
{assert(s);assert(pos >= 0 && pos<=s->size);CheckCapacity(s);for (int i = s->size; i > pos; i--){s->a[i] = s->a[i - 1];}s->a[pos] = x;s->size++;}void SeqListdel(SL* s, int pos)
{assert(s);assert(pos >= 0 && pos < s->size);for (int i = pos; i < s->size-1; i++){s->a[i] = s->a[i + 1];}s->size--;}

 上面的两段代码包含了顺序表的,格式化,销毁,头插,尾插,头删,尾删,指定位置插入。


通讯录的实现

首先我们要知道通讯录里有什么?我们当然知道里面有联系人的姓名,性别,年龄,电话,地址。

我们假设这个通讯录就储存这么些信息,那我们这些信息是不是我们所说的数据,前面我们往顺序表里储存的是整形数据那么现在我们数据类型改为这些信息不就好了,可能有人就会想这些信息有那么多,那我们的数据变量要一个一个建立出来吗?当然不是,代码要求的是简便,既然这些信息可能是不同数据类型,而且还不少,那么我们就可以用一个自定义类型-----结构体来作为数据类型的变量,然后结构体内部就可以储存这些信息,这样一来就简单多了。如下图所示:


有了大概的思路,然后就开始封装顺序表,然后就让它成为一个成熟的通讯录。

创建通讯录的信息结构体和通讯录节点

具体思路:首先我们在建立两个文件一个“Contact.c”,“Contact.h”文件,其中在“Contact.h”文件里定义好储存联系人信息的结构体和声明函数和头文件,

“Contact.h”

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define MAX_NAME 18
#define MAX_TEL 18
#define MAX_GENDER  10
#define MAX_ADDR 100typedef struct Contact
{char name[MAX_NAME];char gender[MAX_GENDER];int age;char tel[MAX_TEL];char addr[MAX_ADDR];}con;typedef struct SEQlist Ct;//通讯录的初始化
void ContactInit(Ct* pf);//通讯录的销毁
void ContactDestory(Ct* pf);//通讯录数据的添加
void ContactAdd(Ct* pf);//通讯录数据的删除
void ContactDel(Ct* pf,char arr[]);//通讯录的展示
void ContactShow(Ct* pf);//通讯录的查找
void ContactFind(Ct* pf,char arr[]);

 这里我们加了一个typedef struct SEQLIST Ct;是为了让人一眼看出这是通讯录而不是一个普通的顺序表,也让我们能够好理解一些。

以及在“Contact.c”文件里写下完成通讯录的一些基本功能的代码。

那么我们可以写的通讯录功能有哪些呢?

通讯录的格式化

void ContactInit(Ct* pf)
{Init(pf);}
void Init(SL* s)
{s->a = NULL;s->size = 0;         // 表示数组中存储了多少个数据s->capacity = 0;   数组实际能存数据的空间容量是多大 }

 

其实这里的格式化就是将顺序表给格式化,会先将结构体数组a给赋为NULL,然后将容量

(int capacity)和有效数据数目(int size)置为0 

通讯录的销毁

void ContactDestory(Ct* pf)
{SeqListDestory(pf);}
void SeqListDestory(SL* s)
{free(s->a);s->a = NULL;s->capacity = s->size = 0;
}

 通讯录的销毁也是一样的,直接调用链表的销毁就好了

通讯录数据的添加

void ContactAdd(Ct* pf)
{assert(pf);struct Contact a;printf("请输入你要添加联系人的姓名\n");scanf("%s", a.name);printf("请输入你要添加联系人的性别\n");scanf("%s", a.gender);printf("请输入你要添加联系人的年龄\n");scanf("%d", &(a.age));printf("请输入你要添加联系人的电话\n");scanf("%s", a.tel);printf("请输入你要添加联系人的地址\n");scanf("%s", a.addr);//往联系人里添加数据SeqListPushBack(pf, a);}
void SeqListPushBack(SL* s, Datetype x)
{CheckCapacity(s);s->a[s->size] = x;s->size++;
}

 因为要添加数据,那我们首先就需要获取这个数据,这里我们运用scanf来进行获取数据放到我们用来储存数据的结构体(struct Contact)中,然后再运用链表的尾插功能,将数据添加到我们的顺序表中。

通讯录数据的删除

我们要删除通讯录里的联系人,首先就得核对通讯录里是否有我们操作者想要删除的人,所以这里我们在写一个函数来进行找人,如果找到此人则返回此人在数组a的下标。,没有则打印“未找到该联系人”,并返回-1;然后在ContactDel中进行一下判断是否为-1,如果不为-1,就执行链表中的指定位置删除

//找到某位联系人的位置
int Findname(Ct* pf,char arr[])
{assert(pf);int i = 0;for (i = 0; i < pf->size; i++){if (strcmp(arr, pf->a[i].name) == 0){return i;}}printf("未找到该联系人\n");}
//删除联系人
void ContactDel(Ct* pf)
{assert(pf);int arr[MAX_NAME];scanf("%s", arr);printf("请输入你要删除的联系人: ");int find=Findname(pf,arr);if (find != -1){SeqListdel(pf, find);}}

 

void SeqListdel(SL* s, int pos)
{assert(s);assert(pos >= 0 && pos < s->size);for (int i = pos; i < s->size-1; i++){s->a[i] = s->a[i + 1];}s->size--;}

 

通讯录的查找

这里我们scanf输入想要查找的联系人,调用查找函数返回得到该联系人的下标,之后再打印出来即可。

//找到某位联系人的位置
int Findname(Ct* pf,char arr[])
{assert(pf);int i = 0;for (i = 0; i < pf->size; i++){if (strcmp(arr, pf->a[i].name) == 0){return i;}}printf("未找到该联系人\n");}
void ContactFind(Ct* pf)
{assert(pf);char arr[MAX_NAME];printf("请输入你要查找的联系人: ");scanf("%s", arr);int find=Findname(pf,arr);if (find != -1){printf("找到该联系人了,其信息如下:\n");printf("姓名    性别     年龄     电话       地址\n");printf("%s      %s       %d        %s         %s",pf->a[find].name, pf->a[find].gender, pf->a[find].age, pf->a[find].tel, pf->a[find].addr);}

 

通讯录的展示

这里就是遍历顺序表,将每个数据给打印出来,只要注意打印格式即可,想要美观可以调好打印的距离。


//通讯录的展示
void ContactShow(Ct* pf)
{assert(pf);int i = 0;for (i = 0; i < (pf->size); i++){printf("姓名    性别     年龄     电话       地址\n");printf("%s      %s       %d        %s         %s\n",pf->a[i].name, pf->a[i].gender, pf->a[i].age, pf->a[i].tel, pf->a[i].addr);}}


可能有人会问,我储存的数据程序一结束,数据就不见了,这还叫通讯录吗?这样确实显得这个通讯录有点鸡肋,所以我们现在就可以再写一个函数,将数据放到文本文件中进行储存

void SaveContact(Ct* con) {FILE* pf = fopen("contact.txt", "wb");if (pf == NULL) {perror("fopen error!\n");return;}//将通讯录数据写⼊⽂件for (int i = 0; i < con->size; i++){fwrite(con->a + i, sizeof(PeoInfo), 1, pf);}printf("通讯录数据保存成功!\n");
}

当然这里是以二进制文件储存的。 

这些功能都是在“Contact.c”里来完成的。


常常会回顾努力的自己,所以要给自己的努力留下足迹。

为今天努力的自己打个卡,留个痕迹吧

                                                                                                        2024.04.20     小闭
 

                          


http://www.ppmy.cn/embedded/4720.html

相关文章

CSS3 立体 3D 变换

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍CSS3 立体 3D 变换&#x1f48e;1 坐标轴&#x1f48e;2 perspective 透视视…

全量知识系统 程序详细设计 定稿 之1 (QA SmartChat )

Q1. 从今天开始&#xff0c;我们进入到全量知识系统&#xff08;简称“全知系统”&#xff09;的程序详细设计的 整理成文阶段--“定稿”&#xff08;或“成熟”&#xff09;阶段&#xff08;相应的&#xff0c;前一阶段可以称为程序详细设计的“构思”&#xff08;或“喂养”&…

数据输入输出流(I/O)

文章目录 前言一、数据输入输出流是什么&#xff1f;二、使用方法 1.DataInputStream类2.DataOutoutStream类3.实操展示总结 前言 数据输入输出流也是将文件输入输出流打包后使用的对象。相比于文件输入输出流&#xff0c;数据输入输出流提供了简单易用的方法去操作不同类型的数…

javaWeb项目-智慧餐厅点餐管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JavaScript Java…

区块链安全应用----压力测试

通过Caliper进行压力测试程序 1.环境配置 第一步. 配置基本环境 部署Caliper的计算机需要有外网权限&#xff1b;操作系统版本需要满足以下要求&#xff1a;Ubuntu > 16.04、CentOS > 7或MacOS > 10.14&#xff1b;部署Caliper的计算机需要安装有以下软件&#xff…

NLP_知识图谱_三元组实战

文章目录 三元组含义如何构建知识图谱模型的整体结构基于transformers框架的三元组抽取baselinehow to use预训练模型下载地址训练数据下载地址 结构图代码及数据bertconfig.jsonvocab.txt datadev.jsonschemas.jsontrain.jsonvocab.json 与bert跟data同个目录model.pytrain.py…

卷积神经网络(LeNet5实现对Fashion_MNIST分类

参考6.6. 卷积神经网络&#xff08;LeNet&#xff09; — 动手学深度学习 2.0.0 documentation (d2l.ai) ps&#xff1a;在这里预备使用pythorch 1.对 LeNet 的初步认识 总的来看&#xff0c;LeNet主要分为两个部分&#xff1a; 卷积编码器&#xff1a;由两个卷积层组成; …

理解汇编中的CALL指令和参数传递

本节视频学习下载地址&#xff1a;链接&#xff1a;https://pan.quark.cn/s/3c511241b7cf 在汇编语言编程中&#xff0c;函数调用是通过​​CALL​​​指令实现的。正确传递参数给函数是编写可靠汇编程序的关键。在本文中&#xff0c;我们将深入探讨如何在x86汇编中使用栈来传…