当前位置: 首页 > news >正文

C语言顺序表实战:打造简易通讯录

目录

一、前景提要及灵感来源

二、预备知识

2.1顺序表的定义与结构特点

2.2顺序表的结构

2.3顺序表的基本操作

三、定义通讯录的数据结构

四、通讯录操作方法设计

4.1通讯录初始化

4.2通讯录的销毁

4.3通讯录的添加方法

4.4通讯录的删除

4.5通讯录的展示

4.6通讯录的查找

4.7通讯录的修改

五、用户交互功能与菜单设计

六、通讯录各项功能测试

6.1通讯录初始化测试

6.2通讯录的添加测试

6.3通讯录的删除和展示测试

6.4通讯录的修改测试

七、结语


一、前景提要及灵感来源

学习了顺序表定义与结构以及顺序表相关方法之后,很难不承认,顺序表是一个很好的工具,以数组为底层结构,但是却自带了很多数组没有的方法例如头插(PushFront),尾插(PushBack),头删(PopFront)等等,那么顺序表具体有什么用,为了巩固顺序表的相关特点和方法,笔者将全过程讲解基于顺序表实现简易通讯录。

通讯录的需求分析,功能需求(添加联系人,删除联系人,查找联系人,修改联系人信息),数据存储需求(姓名,电话,年龄,地址等)。不难发现在实现功能需求上,顺序表具有明显的优势,接下来的许多方法实现实际上是对顺序表方法的封装复用。

如果我们以单个联系人为元素存储,那么顺序表不就被我们改装成通讯录了嘛?

二、预备知识

2.1顺序表的定义与结构特点

顺序表是线性表的一种,以数组为底层结构,对数组进行封装,实现了增删查改等接口。顺序表的数据类型是结构体(用结构体定义)。顺序表分为静态顺序表和动态顺序表,静态顺序表的底层为一个定长数组,动态顺序表包含了一个指向某一数据类型数组的指针,如果是以整形数据为底层结构那么该指针就是int*类型,如果是以字符数组为底层结构那么该指针就是char*类型,当然也可以是以自定义类型为元素进行存储,那么相应的指针就是你说定义的结构体的指针类型。

2.2顺序表的结构

定义顺序表的结构:

typedef int DataType #define N 10 //静态顺序表 strcut STSeqList { Datatype arr[N];//定长数组 int size;//有效数据个数 } //动态顺序表 typedef struct SeqList { DataType* arr; int size; //有效数据个数 int capacity; //顺序表空间大小 }SL;

2.3顺序表的基本操作

SeqList.h

​ //定义顺序表的结构 //静态顺序表 //封装了一个定长数组 typedef peoInfo DataType;//方便后续类型替换 //#define N 100 //struct SeqList //{ // DataType arr[N]; // //}; //动态顺序表 typedef struct SeqList { DataType* arr; int size; int capacity; }SL; //顺序表的初始化方法 void SLInit(SL* ps); //传址调用,才能通过这个地址解引用访问顺序表结构体 //顺序表的销毁方法 void SLDestory(SL* ps); //检查空间大小方法 void SLCapacityCheck(SL* ps); //顺序表增删查改操作 //-------增加操作-------- //尾插 void SLPushBack(SL* ps, DataType X); //头插 void SLPushFront(SL* ps, DataType X); //顺序表打印方法 //void SLPrint(SL a); //头删 void SLPopFront(SL* ps); //尾删 void SLPopBack(SL* ps); //指定位置之前插入数据 void SLInsert(SL* ps, int pos, DataType X); //删除指定位置的数据 void SLErase(SL* ps, int pos); //查找指定数据 //int SLFind(SL* ps, DataType X); ​

SeqList.c

#define _CRT_SECURE_NO_WARNINGS #include "SeqList.h" #include <assert.h> //顺序表初始化方法 void SLInit(SL* ps) { ps->arr = NULL; ps->size = ps->capacity = 0; } //顺序表销毁方法 void SLDestory(SL* ps) { assert(ps); if (ps->arr)//ps->arr!=NULL { free(ps->arr); } ps->arr = NULL; ps->size = ps->capacity = 0; } //空间检查方法(不够时会申请空间) void SLCapacityCheck(SL* ps) { if (ps->size == ps->capacity) { //申请空间用malloc realloc还是calloc //realloc用来增容 //三目表达式↓ int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//空间为空申请四个,不够就加倍 DataType* tmp = (DataType*)realloc(ps->arr, newcapacity * sizeof(DataType)); if (tmp == NULL)//用一个临时的tmp变量,解决realloc失败,原空间变NULL的问题 { perror(tmp); exit(1); } ps->arr = tmp; ps->capacity = newcapacity; } } //尾插方法 void SLPushBack(SL* ps,DataType X) { //一种温柔的解决方式,判断是否是空指针 //if (ps == NULL) //{ // return; //} assert(ps);//暴力的解决方式 //插入前检查空间 SLCapacityCheck(ps); ps->arr[ps->size++] = X; } //头插方法 void SLPushFront(SL* ps, DataType X) { assert(ps); SLCapacityCheck(ps);//头插也要检查空间 //头插需要先让顺序表中的已有的数据整体往后挪动一位 for (int i = ps->size; i > 0; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[0] = X; ps->size++;//SLPrint打印方法依赖于size,size要改变 } void SLPrint(SL a) { for (int i = 0; i < a.size; i++) { printf("%d ", a.arr[i]); } printf("\n"); } void SLPopBack(SL* ps) { assert(ps); assert(ps->size); --ps->size;//不影响增删查改,所以直接让size-1 } void SLPopFront(SL* ps) { assert(ps); assert(ps->size); for (int i = 0; i < ps->size-1; i++) { ps->arr[i] = ps->arr[i + 1]; } --ps->size; } void SLInsert(SL* ps, int pos, DataType X) { assert(ps); assert(pos >= 0 && pos <= ps->size);//pos大于等于且要小于等于有效数据个数 //先检查空间够不够,不够就申请空间 SLCapacityCheck(ps); //pos及以后的数据整体往后移动一位 for (int i = ps->size; i > pos; i--) { ps->arr[i] = ps->arr[i - 1]; } ps->arr[pos] = X; ps->size++; } void SLErase(SL* ps, int pos) { assert(ps); assert(pos >= 0 && pos < ps->size);//和SLInsert不同,因为size位置上无数据不可删除 //pos以后的数据整体向前移动一位 for (int i = pos; i < ps->size - 1; i++) { ps->arr[i] = ps->arr[i + 1];//最后一步是arr[size-2]=arr[size-1]; } ps->size--; } int SLFind(SL* ps, DataType X) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->arr[i] == X) { //找到了 return i; } } //没找到 return -1; }

三、定义通讯录的数据结构

定义联系人结构体,包含姓名、年龄、电话等信息。

//联系人结构体包含了姓名、性别、年龄、电话、地址 typedef struct PersonInfo { char name[NAME_MAX]; char sex[SEX_MAX]; int age; char tel[TEL_MAX]; char addr[ADDR_MAX]; }peoInfo;//改名字方便使用 typedef struct SeqList contact;//整体改名为contact区别出来方便使用

我们采用动态顺序表来实现:

typedef peoInfo DataType; typedef struct SeqList { DataType* arr; int size; int capacity; }SL;

四、通讯录操作方法设计

4.1通讯录初始化

因为通讯录的初始化实际上就是顺序表的初始化所以我们直接调用了SeqList.h、SeqList.c顺序表方法中SLInit()

//通讯录的初始化实际上就是顺序表的初始化 //直接调用顺序表初始化方法 void InitContact(contact* con) { SLInit(con); }

4.2通讯录的销毁

和通讯录的初始化类似都是本质顺序表的初始化或者销毁,所以也是直接调用了SLDestroy()方法:

//通讯录的销毁 void DestroyContact(contact* con) { SLDestory(con); }

4.3通讯录的添加方法

通讯录的核心功能之一:能够添加联系人以及联系人各项信息,如名字,电话等,与上文顺序表的添加不同在于,我们通讯录储存的是自定义类型,所以在添加到通讯录这种顺序表前,我们先要对自己要添加的联系人结构体进行初始化:最后是直接复用了顺序表中的SLPushBack()尾插方法。

//通讯录的添加 void AddContact(contact* con) { peoInfo info; printf("请输入要添加的联系人姓名:\n"); scanf("%s", info.name); printf("请输入要添加的联系人性别:\n"); scanf("%s", info.sex); printf("请输入要添加的联系人年龄:\n"); scanf("%d", &info.age); printf("请输入要添加的联系人电话:\n"); scanf("%s", info.tel); printf("请输入要添加的联系人地址:\n"); scanf("%s", info.addr); //以上代码实现创建一个联系人变量并通过输入初始化好 //为后面的pushback到通讯录顺序表做准备 SLPushBack(con, info); }

4.4通讯录的删除

一个简单的通讯录还需要具备能够删除指定联系人的功能,为了实现这一功能:删除指定联系人,指定的意思也就是我们要提供一些这个我们要删除联系人的信息,供程序查找通讯录中有无这个联系人数据,如果有就返回联系人在通讯录的位置(下标),如果没有返回一个小于0的值(如-1),标记为通讯录没有要删除的联系人的数据的依据。通过名字查找联系人代码如下:

//按照名字查找方法,查出数据所对应的下标,返回下标 //如果数据不存在就返回一个小于0的值,-1 int FindByName(contact* con, char name[]) { for (int i = 0; i < con->size; i++) { if (0 == strcmp(con->arr[i].name, name)) { return i; } } return -1; }

删除方法如下:

//通讯录的删除 void DelContact(contact* con) { //删除联系人 //删除指定的联系人 //先要找到联系人位置 //通过联系人下标删除 //如何联系人不存在就返回一个-1,代表联系人不存在 char name[NAME_MAX]; printf("请输入要删除的联系人姓名:\n"); scanf("%s", name); int find = FindByName(con, name); if (find == -1) { printf("要删除的联系人数据不存在!\n"); return; } SLErase(con,find); printf("删除成功!\n"); }

4.5通讯录的展示

前面所写到的方法都是需要及时的测试,其中为了直观的展示通讯录的添加,删除是否成功,我们需要将通讯录中联系人信息打印出来:

//通讯录的展示 void ShowContact(contact* con) { //先打印表头 printf("%s %s %s %s %s\n", "姓名.", "性别", "年龄", "电话", "地址"); for (int i = 0; i < con->size; i++) { printf("%3s %3s %3d %3s %3s\n",//手动调整一下格式 con->arr[i].name, con->arr[i].sex, con->arr[i].age, con->arr[i].tel, con->arr[i].addr); } }

4.6通讯录的查找

有时候我们不需要展示出所有联系人的信息,只需要找到某一个联系人,如果该联系人在通讯录中就能被我们找到,并且打印出他的各项信息:

//通讯录的查找 void FindContact(contact* con) { char name[NAME_MAX]; printf("请输入要查找的联系人姓名:\n"); scanf("%s", name); int find=FindByName(con, name); if (find < 0) { printf("要查找的联系人数据不存在!\n"); return; } printf("%s %s %s %s %s\n", "姓名.", "性别", "年龄", "电话", "地址"); printf("%3s %3s %3d %3s %3s\n",//手动调整一下格式 con->arr[find].name, con->arr[find].sex, con->arr[find].age, con->arr[find].tel, con->arr[find].addr); }

4.7通讯录的修改

一个常见的场景是在我们将某个联系人添加到通讯录中,我们可能存错了他的名字或电话等信息,那么我们希望我们的通讯录具备通讯录的修改功能:

//通讯录的修改 void ModifyContact(contact* con) { char name[NAME_MAX]; printf("请输入你要修改的联系人的姓名!\n"); scanf("%s", name); int find = FindByName(con, name); if (find < 0) { printf("要修改的联系人数据不存在!\n"); } //直接修改 printf("请输入新的姓名:\n"); scanf("%s", con->arr[find].name); printf("请输入新的性别:\n"); scanf("%s", con->arr[find].sex); printf("请输入新的年龄:\n"); scanf("%d", &con->arr[find].age); printf("请输入新的电话:\n"); scanf("%s", con->arr[find].tel); printf("请输入新的地址:\n"); scanf("%s", con->arr[find].addr); printf("修改成功!\n"); }

五、用户交互功能与菜单设计

当程序运行起来时,我们期望有一个菜单能直观地提示用户通过输入什么数字可以调用什么功能,所以我们需要设计一个简单菜单:

void menu() { printf("********************通讯录********************\n"); printf("**********************************************\n"); printf("*********1.增加联系人 2.删除联系人**********\n"); printf("*********3.修改联系人 4.查找联系人**********\n"); printf("*********5.展示联系人 0. 退出 **********\n"); }

效果展示:

我们还需要设计一些交互功能:通过do_while和switch语句实现:

do { menu(); printf("请选择您的操作:\n"); scanf("%d", &op); switch (op) { case 1: AddContact(&con); break; case 2: DelContact(&con); break; case 3: ModifyContact(&con); break; case 4: FindContact(&con); break; case 5: ShowContact(&con); break; default: break; } } while (op != 0);

六、通讯录各项功能测试

6.1通讯录初始化测试

初始化测试成功,通讯录的有效数据个数和容量都初始化为0.

6.2通讯录的添加测试

6.3通讯录的删除和展示测试

输入要删除的联系人数据成功,提示我们删除成功。让我们在监视窗口看看:size由原来的1减到0,说明有效数据个数减少了一位,也就是联系人数据被删除了一位。

由于这里我们只插入了一个元素所以,只让size-1即可,我们就访问不到该联系人信息。调用从通讯录展示方法ShowContact方法也同样可以直观地看到,联系人数据已经被删除了:

6.4通讯录的修改测试

再次运行程序,在展示之前我们需要先添加两个联系人数据:

程序提示我们修改成功后,在下一次操作中我们需调用展示方法,验证修改结果。

通讯录的修改测试也成功通过。

七、结语

以上便是在控制台实现的通讯录交互设计和基本操作方法,如果需要在程序退出,也能将联系人信息保存下来,我们需要配合文件操作方法,将联系人信息写到文件中,真正地保存下来。文章到这里就结束了。

代码和文章可能存在纰漏,欢迎批评、指正和交流。共同进步!

完整代码仓库:code_for_study: 学习代码

http://www.jsqmd.com/news/328063/

相关文章:

  • 【万金油-合同管理】信息系统项目管理师案例分析
  • Flutter for HarmonyOS 开发指南(五):实现tabbar主菜单功能
  • Flutter for HarmonyOS 开发指南(四):实现上拉加载,下拉刷新能力
  • 2026年超低温防水卷材品牌深度评估:6家顶尖公司综合解析
  • 【windows工具】Inspect工具安装和使用
  • SpringBoot+Vue Spring Boot企业员工薪酬关系系统管理平台源码【适合毕设/课设/学习】Java+MySQL
  • Java Web Spring Boot在线远程考试系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • 2026年驻马店玉米种子服务商综合评估与优选指南
  • 【毕业设计】SpringBoot+Vue+MySQL Spring Boot疗养院管理系统平台源码+数据库+论文+部署文档
  • Hive数据血缘分析:追踪数据来源的完整方案
  • Java Web Spring Boot企业员工薪酬关系系统系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • SpringBoot+Vue +乡政府管理系统管理平台源码【适合毕设/课设/学习】Java+MySQL
  • Java SpringBoot+Vue3+MyBatis Spring Boot在线远程考试系统系统源码|前后端分离+MySQL数据库
  • 高校教师电子名片系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
  • 【毕业设计】SpringBoot+Vue+MySQL +乡政府管理系统平台源码+数据库+论文+部署文档
  • OpenClaw Windows 安装与 Debug 最终版教程(适用于 MiniPC i3-N305 / 无 GPU)
  • C语言 结构体
  • 【Linux04】 Linux基础指令完结与Linux权限初识(一)
  • 聚焦洪山:2026年幼儿英语兴趣班专业选择面面观
  • 【2025最新】基于SpringBoot+Vue的房屋交易平台管理系统源码+MyBatis+MySQL
  • 2026东湖高新区英语辅导机构深度测评与六强推荐
  • 抢不到 Coding Plan?本地 LiteLLM 让 GLM 也能跑 Claude Code【手把手本地部署教程!】
  • [Unix Pipe] find . -maxdepth 1 -type f -name *.torrent | transmission-show
  • 2026年青少年内衣权威企业盘点:科技健康引领新选择
  • 十六、用 GPT2 中文古文模型实现经典名句续写
  • 十四、基于 BERT 的微博评论情感分析模型训练实践
  • 260131 今年已经过去一个月了
  • 大数据领域 HDFS 的数据一致性保障
  • 探索大数据领域 RabbitMQ 的多租户模式
  • 大数据领域数据架构的财务管理应用