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

【数据结构】顺序表+回调函数

深入理解顺序表的回调函数设计与实现

在数据结构的学习中,顺序表是最基础的线性结构之一。而回调函数的引入,能让顺序表的操作更具灵活性、可扩展性。本文将以 C 语言为例,完整分析如何将顺序表与回调函数结合,并分享从需求到实现的全过程。

顺序表与回调函数

  • 深入理解顺序表的回调函数设计与实现
    • 一、为什么要在顺序表中使用回调函数?
    • 二、回调函数的基础:函数指针
    • 三、顺序表操作的 “回调化” 实现
      • 1. 顺序表初始化与内存管理
      • 2. 增操作的实现(头插、尾插、指定位置插)
      • 3. 删操作的实现(头删、尾删、指定位置删)
      • 4. 辅助操作(打印、销毁)
    • 四、回调函数的调用逻辑(test.c)
    • 五、回调函数的优势与扩展场景
      • 1. 核心优势
      • 2. 扩展场景
    • 六、总结
    • 七、总代码
      • ceqList.h头文件
      • SeqList.c文件
      • test.c文件

一、为什么要在顺序表中使用回调函数?

在传统的顺序表代码中,我们通常直接调用SLPushFrontSLPopBack等函数来完成增删操作。这种写法虽然直接,但存在两个明显的不足:

  • 耦合性高:调用逻辑和操作实现紧密绑定,若要修改操作(比如添加日志、统计性能),需改动所有调用处。
  • 扩展性差:新增操作或替换操作逻辑时,需要大面积修改代码。

而 ** 回调函数(通过函数指针间接调用函数)** 可以解决这些问题 —— 它能让 “调用逻辑” 和 “操作实现” 解耦,让代码更灵活、易维护。

二、回调函数的基础:函数指针

在 C 语言中,函数指针是实现回调的核心。它本质是一个 “指向函数的指针变量”,可以像普通变量一样赋值、传递。

我们先定义几个与顺序表操作匹配的函数指针类型(以SeqList.h为例):

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>typedef int ListDateType;typedef struct CeqList
{ListDateType* arr;int size;int capecity;
}SL;// 函数指针类型:头插操作
typedef void (*SLPushFrontFunc)(SL*, ListDateType);
// 函数指针类型:尾插操作
typedef void (*SLPushBackFunc)(SL*, ListDateType);
// 函数指针类型:头删操作
typedef void (*SLPopFrontFunc)(SL*);
// 函数指针类型:尾删操作
typedef void (*SLPopBackFunc)(SL*);
// 函数指针类型:指定位置插入操作(假设SLPush的签名是 void SLPush(SL*, int 位置, SLDataType 元素))
typedef void (*SLPushPosFunc)(SL*, int, ListDateType);
// 函数指针类型:指定位置删除操作(假设SLPop的签名是 void SLPop(SL*, int 位置))
typedef void (*SLPopPosFunc)(SL*, int);//顺序表基本操作类型
//定义顺序表
void CaqListInit(SL* ps);//尾插
void SLPushBack(SL* ps, ListDateType x);//头插
void SLPushFront(SL* ps, ListDateType x);//随机加入元素
void SLPush(SL* ps, int pos, ListDateType x);//随机删除
void SLPop(SL* ps, int pos);//尾删元素
void SLPopBack(SL* ps);//头删
void SLPopFront(SL* ps);//顺序表销毁
void SLDestory(SL* ps);//打印元素
void SLPrint(SL ps);

三、顺序表操作的 “回调化” 实现

接下来,我们需要实现顺序表的基础操作(这些操作是回调的 “具体执行逻辑”)。

1. 顺序表初始化与内存管理

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"//定义顺序表
void CaqListInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capecity = 0;
}//判断内存够不够
void SLCheckCapecity(SL* ps)
{if (ps->capecity == ps->size){//第一种情况,都为0//用三目运算符int NewCapecity = ps->capecity == 0 ? 4 : ps->capecity * 2;//第二种情况,空间不够//开辟新空间,tmp来接受ListDateType* tmp = (ListDateType*)realloc(ps->arr, NewCapecity * sizeof(ListDateType));if (tmp == NULL){perror("realloc");exit(1);}//空间申请成功ps->arr = tmp;ps->capecity = NewCapecity;}
}

2. 增操作的实现(头插、尾插、指定位置插)

//头插
void SLPushFront(SL* ps, ListDateType x)
{assert(ps);SLCheckCapecity(ps);for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}
//尾插
void SLPushBack(SL* ps, ListDateType x)
{assert(ps);SLCheckCapecity(ps);ps->arr[ps->size] = x;ps->size++;
}

3. 删操作的实现(头删、尾删、指定位置删)

//头删
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
//尾删
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);ps->size--;
}
//随机删除
void SLPop(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i - 1] = ps->arr[i];//arr[pos] = arr[pos + 1]}ps->size--;
}

4. 辅助操作(打印、销毁)

//打印元素
void SLPrint(SL ps)
{for (int i = 0; i < ps.size; i++){printf("%d ", ps.arr[i]);}printf("\n");
}//顺序表的销毁
void SLDestory(SL* ps)
{if (ps->arr){free(ps->arr);ps->arr = NULL;}ps->capecity = ps->size = 0;
}

四、回调函数的调用逻辑(test.c)

现在,我们在测试代码中通过函数指针来调用上述操作,实现 “回调” 逻辑。

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"void menu()
{printf("***********************************\n");printf("**** 1.头插  2.尾插  3.随机插入 ****\n");printf("**** 4.头删  5.尾删  6.随机删除 ****\n");printf("**** 0.退出                    ***\n");printf("**********************************\n");
}void SeqListTest()
{SL sl;CaqListInit(&sl);// 定义函数指针,指向具体的操作函数SLPushFrontFunc pushFront = SLPushFront;SLPushBackFunc pushBack = SLPushBack;SLPushPosFunc pushPos = SLPush;SLPopFrontFunc popFront = SLPopFront;SLPopBackFunc popBack = SLPopBack;SLPopPosFunc popPos = SLPop;int input = 0;do{menu();scanf("%d", &input);ListDateType val = 0;int pos = 0;switch (input){case 0:printf("退出顺序表\n");break;case 1:printf("请输入要添加的元素:");scanf("%d", &val);pushFront(&sl, val); // 回调头插函数break;case 2:printf("请输入要添加的元素:");scanf("%d", &val);pushBack(&sl, val); // 回调尾插函数break;case 3:printf("请输入位置和元素(位置 元素):");scanf("%d %d", &pos, &val);pushPos(&sl, pos, val); // 回调指定位置插入函数break;case 4:popFront(&sl); // 回调头删函数break;case 5:popBack(&sl); // 回调尾删函数break;case 6:printf("请输入要删除的位置:");scanf("%d", &pos);popPos(&sl, pos); // 回调指定位置删除函数break;default:printf("输入错误,请重新输入\n");break;}if (input != 0) {SLPrint(sl); // 每次操作后打印顺序表}} while (input);SLDestory(&sl);}int main()
{SeqListTest();return 0;
}

五、回调函数的优势与扩展场景

1. 核心优势

  • 解耦性:调用逻辑(test.c)和操作实现(SeqList.c)完全解耦。修改操作逻辑时,无需改动调用处。
  • 灵活性:可在运行时动态替换函数指针指向的函数。例如,若要给 “头插” 添加日志,只需实现一个带日志的SLPushFront变种,再将函数指针重新绑定即可。
```c
// 带日志的头插变种
void SLPushFront_WithLog(SL* psl, SLDataType x) {printf("【日志】执行头插操作,元素:%d\n", x);SLPushFront(psl, x); // 调用原头插逻辑
}// 在测试代码中替换函数指针
SLPushFrontFunc pushFront = SLPushFront_WithLog; // 后续调用将带有日志
  • 可扩展性:新增操作时,只需定义新的函数指针类型和实现函数,调用逻辑几乎无需改动。

2. 扩展场景

回调函数在实际开发中还有更多应用场景:

  • 自定义比较 / 排序:比如实现一个支持自定义比较函数的顺序表排序。
  • 事件驱动:在顺序表操作前后触发自定义 “事件”(如统计操作次数、性能监控)。
  • 多态模拟:在 C 语言中模拟面向对象的 “多态” 特性,不同操作对应不同的函数实现。

六、总结

通过将顺序表操作与回调函数结合,我们实现了代码解耦、灵活扩展的目标。核心步骤是:定义函数指针类型 → 实现基础操作 → 通过函数指针调用操作。

这种设计不仅让顺序表的代码结构更优雅,也为后续复杂功能的扩展(如日志、性能监控、自定义策略)打下了基础。掌握回调函数的思路,也能帮助你在其他数据结构(如链表、栈、队列)中写出更灵活的代码。

七、总代码

ceqList.h头文件

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>typedef int ListDateType;typedef struct CeqList
{ListDateType* arr;int size;int capecity;
}SL;// 函数指针类型:头插操作
typedef void (*SLPushFrontFunc)(SL*, ListDateType);
// 函数指针类型:尾插操作
typedef void (*SLPushBackFunc)(SL*, ListDateType);
// 函数指针类型:头删操作
typedef void (*SLPopFrontFunc)(SL*);
// 函数指针类型:尾删操作
typedef void (*SLPopBackFunc)(SL*);
// 函数指针类型:指定位置插入操作(假设SLPush的签名是 void SLPush(SL*, int 位置, SLDataType 元素))
typedef void (*SLPushPosFunc)(SL*, int, ListDateType);
// 函数指针类型:指定位置删除操作(假设SLPop的签名是 void SLPop(SL*, int 位置))
typedef void (*SLPopPosFunc)(SL*, int);//定义顺序表
void CaqListInit(SL* ps);//尾插
void SLPushBack(SL* ps, ListDateType x);//头插
void SLPushFront(SL* ps, ListDateType x);//随机加入元素
void SLPush(SL* ps, int pos, ListDateType x);//随机删除
void SLPop(SL* ps, int pos);//尾删元素
void SLPopBack(SL* ps);//头删
void SLPopFront(SL* ps);//顺序表销毁
void SLDestory(SL* ps);//打印元素
void SLPrint(SL ps);

SeqList.c文件

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"//定义顺序表
void CaqListInit(SL* ps)
{ps->arr = NULL;ps->size = ps->capecity = 0;
}//判断内存够不够
void SLCheckCapecity(SL* ps)
{if (ps->capecity == ps->size){//第一种情况,都为0//用三目运算符int NewCapecity = ps->capecity == 0 ? 4 : ps->capecity * 2;//第二种情况,空间不够//开辟新空间,tmp来接受ListDateType* tmp = (ListDateType*)realloc(ps->arr, NewCapecity * sizeof(ListDateType));if (tmp == NULL){perror("realloc");exit(1);}//空间申请成功ps->arr = tmp;ps->capecity = NewCapecity;}
}//尾插
void SLPushBack(SL* ps, ListDateType x)
{assert(ps);SLCheckCapecity(ps);ps->arr[ps->size] = x;ps->size++;
}//头插
void SLPushFront(SL* ps, ListDateType x)
{assert(ps);SLCheckCapecity(ps);for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}//尾删
void SLPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);ps->size--;
}//头删
void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}//随机加入
void SLPush(SL* ps, int pos, ListDateType x)
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapecity(ps);for (int i = ps->size; i > pos; i--){ps->arr[i] = ps->arr[i - 1]; //arr[pos + 1] = arr[pos]}ps->arr[pos] = x;ps->size++;
}//随机删除
void SLPop(SL* ps, int pos)
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos; i < ps->size - 1; i++){ps->arr[i - 1] = ps->arr[i];//arr[pos] = arr[pos + 1]}ps->size--;
}//打印元素
void SLPrint(SL ps)
{for (int i = 0; i < ps.size; i++){printf("%d ", ps.arr[i]);}printf("\n");
}//顺序表的销毁
void SLDestory(SL* ps)
{if (ps->arr){free(ps->arr);ps->arr = NULL;}ps->capecity = ps->size = 0;
}

test.c文件

#define _CRT_SECURE_NO_WARNINGS
#include"SeqList.h"void menu()
{printf("***********************************\n");printf("**** 1.头插  2.尾插  3.随机插入 ****\n");printf("**** 4.头删  5.尾删  6.随机删除 ****\n");printf("**** 0.退出                    ***\n");printf("**********************************\n");
}void SeqListTest()
{SL sl;CaqListInit(&sl);// 定义函数指针,指向具体的操作函数SLPushFrontFunc pushFront = SLPushFront;SLPushBackFunc pushBack = SLPushBack;SLPushPosFunc pushPos = SLPush;SLPopFrontFunc popFront = SLPopFront;SLPopBackFunc popBack = SLPopBack;SLPopPosFunc popPos = SLPop;int input = 0;do{menu();scanf("%d", &input);ListDateType val = 0;int pos = 0;switch (input){case 0:printf("退出顺序表\n");break;case 1:printf("请输入要添加的元素:");scanf("%d", &val);pushFront(&sl, val); // 回调头插函数break;case 2:printf("请输入要添加的元素:");scanf("%d", &val);pushBack(&sl, val); // 回调尾插函数break;case 3:printf("请输入位置和元素(位置 元素):");scanf("%d %d", &pos, &val);pushPos(&sl, pos, val); // 回调指定位置插入函数break;case 4:popFront(&sl); // 回调头删函数break;case 5:popBack(&sl); // 回调尾删函数break;case 6:printf("请输入要删除的位置:");scanf("%d", &pos);popPos(&sl, pos); // 回调指定位置删除函数break;default:printf("输入错误,请重新输入\n");break;}if (input != 0) {SLPrint(sl); // 每次操作后打印顺序表}} while (input);SLDestory(&sl);}int main()
{SeqListTest();return 0;
}
http://www.dtcms.com/a/536117.html

相关文章:

  • 图像归一化:OpenCV 高效映射 [0,255] → [-1,1] 性能实测
  • allWebPlugin.IE扩展使用介绍
  • C语言输入输出函数完整指南:从基础到高级应用
  • 20.10 多模态PPT生成准确率突破93%!ChatPPT v3.0动态权重技术深度解析
  • 中国移动通信联合会人工智能与元宇宙产业委联席秘书长叶毓睿受聘为“北京设计学会无障碍设计专业委员会指导专家”
  • 励志网站织梦源码建个人网上银行登录入口
  • Nginx清除浏览器缓存的三个缓存响应头的关系详解
  • 14天极限复习软考-day5 软件工程 、UML
  • 基于M4-R1开发板的OpenHarmony开发实战丨创建第一个应用工程
  • 【计算机视觉】Python 验证码图片分割:基于 OpenCV 的字符区域提取实现
  • 时序论文速递:覆盖时间序列预测、分类、异常检测及交叉应用!(10.20-10.24)
  • wordpress 建视频网站福步外贸论坛怎样注册
  • 没有网站可以做的广告联盟家居企业网站建设精英
  • navicat11不支持mysql8.0的加密方式
  • 程序综合实践第五次DP1
  • 架构论文《论系统超融合架构的设计与应用》
  • PCB行业数字化转型样本:兴森科技携手盘古信息MOM系统,实现生产效率跃升
  • ESP32-C3赋能物联网设备,开启产品智能化,乐鑫代理商飞睿科技
  • 奥比中光相机pythonAPI color.py运行报错 (-5:Bad argument) in function ‘cvtColor‘
  • 南和网站建设公司建筑设计网课
  • 网站建设与管理 自考网络营销的四大基础理论
  • 毫秒级自动对焦,超高景深液态镜头在机器视觉检测中的应用
  • 了解一下攻击树(从攻击者的视角审视自身系统)
  • 【MySQL体系】第7篇:MySQL锁机制深度解析与实战
  • 【代码随想录算法训练营——Day50(Day49周日休息)】图论——98.所有可达路径
  • 基于Django的医疗电子仪器系统
  • Django 用户认证流程详解:从原理到实现
  • 新版 vscode 去除快捷键 Ctrl+I 显示 Copilot 的 AI 对话框
  • 工业智能车载台应用在什么场景?有什么功能?
  • ES7243E ADC模拟音频转i2S到 BES I2S1 Master输出播放到SPK精准分析