C语言实现状态机

关于状态机,基础的知识点可以自行理解,讲解的很多,这里主要是想写一个有限状态机FSM通用的写法,目的在于更好理解,移植,节省代码阅读与调试时间,体现出编程之美。

传统的实现方案

  • if...else : 搞一大堆if else, 一个函数写很长很长......

  • swich...case : 也搞一大堆一个函数写很长很长......

 先来看看最近做的一个项目,无线通信协议实现的状态机是什么样子的:

图片

有三种类型的事件:上层下达的命令事件;下层到达的标志和数据传输事件;超时定时器超时事件。有10种状态,关联性很大,复杂了吧,这要是各种if/else的要写到什么时候呢。

在事件中判断状态,在状态中判断事件,横竖两种写法的代码都比较冗长,看起来呢也不大好,一旦增减,就又要动脑子重新梳理一遍,很累的。

怎么去写呢?其状态机原理:在根据当前状态(cur_state) 下,发生事件(event)后,转移到下一个状态号(nxt_state),决定执行的动作(action)。

图片

这里我们首先定义一个结构体如下:

typedef struct {State curState;//当前状态EventID eventId;//事件IDState nextState;//下个状态Action action;//具体表现
}StateTransform;

我们假设有3种状态,这里可以随意增加,状态枚举如下:

typedef enum {state_1=1,state_2,state_3
}State;

我们假设有5个事件,也可以随意增加,事件ID枚举如下:

typedef enum{event_1=1,event_2,event_3,event_4,event_5
}EventID;

将其封装起来在StateMachine中:

typedef struct{State state;int transNum;StateTransform* transform;
}StateMachine;

具体流程:当前状态-有事件触发-跳到下个状态-具体表现,重构代码

StateTransform* findTranss(StateMachine* pSM,  const EventID evt)
{int i;for (i = 0; i < pSM->transNum; i++) {if ((pSM->transform[i].curState == pSM->state) && (pSM->transform[i].eventId == evt)) {return &pSM->transform[i];}}return NULL;
}

状态机实现如下:

void runStateMachine(StateMachine* pSM, EventID evt) {StateTransform* pTrans;pTrans = findTranss(pSM, evt);if (pTrans == NULL){xil_printf( "CurState= %s Do not process enent: %s\r\n", pSM->state,evt);return;}pSM->state = pTrans->nextState;Action act = pTrans->action;if (act == NULL) {xil_printf( "change state to %s. No action\r\n",pSM->state);return;}act(&evt);
}

最后我模拟一些随机事件,我们只需要弄清楚事件ID,状态切换,具体表现就可以了,在代码中就是填写stateTran[] 这个表,一旦有增减事件,状态等等,也不需要再去使用switch/case,特费脑,其代码如下:


int run()
{StateMachine stateMachine;stateMachine.state = state_1;stateMachine.transNum = 7;StateTransform stateTran[] = {{state_1,event_3,state_2,f121},{state_1,event_4,state_2,NULL},{state_2,event_1,state_3,f231},{state_2,event_4,state_2,f221},{state_3,event_2,state_1,f311},{state_3,event_3,state_2,f321},{state_3,event_5,state_3,f331}};stateMachine.transform = stateTran;EventID inputEvent[15] = { event_1, event_2, event_3, event_4, event_5,event_1, event_2, event_3, event_4, event_5,event_1, event_2, event_3, event_4, event_5 };int i;for (i = 0; i < 15; i++) {runStateMachine(&stateMachine, inputEvent[i]);}return 0;
}

最后运行结果如下

图片

总结:

状态机应用很广泛,也可以锻炼我们写代码的逻辑思维,看清问题的本质,写的代码才能赏心悦目,希望大家能够多多指点,找到编程的乐趣,欣赏到编程之美。

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

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

相关文章

Unittest 笔记:unittest拓展生成HTM报告发送邮件

HTMLTestRunner 是一个unitest拓展可以生成HTML 报告 下载地址&#xff1a;GitHub: https://github.com/defnnig/HTMLTestRunner HTMLTestRunner是一个独立的py文件&#xff0c;可以放在Lib 作为第三方模块使用或者作为项目的一部分。 方式1&#xff1a; 验证是否安装成功&…

基于Android的课程教学互动系统 微信小程序uniapp

教学互动是学校针对学生必不可少的一个部分。在学校发展的整个过程中&#xff0c;教学互动担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类教学互动程序也在不断改进。本课题所设计的springboot基于Android的教学互动系统&#xff0c;使用SpringBoot框架&am…

Android TV开发之VerticalGridView

Android TV应用开发和手机应用开发是一样的&#xff0c;只是多了焦点控制&#xff0c;即选中变色。 androidx.leanback.widget.VerticalGridView 继承 BaseGridView &#xff0c; BaseGridView 继承 RecyclerView 。 所以 VerticalGridView 就是 RecyclerView &#xff0c;使…

mysql下载

网址 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 2、选择MSI进行安装 3、这里我选择离线安装 4、这里我选择直接下载 5、等待下载安装即可

【无法联网】电脑wifi列表为空的解决方案

打开电脑, 发现wifi列表为空, 点击设置显示未连接 首先检查是不是网卡驱动有问题, cmd, devmgmt.msc 找到网络适配器, 看看网卡前面是否有感叹号, 如果没有则说明网卡没问题, 有问题则重装驱动 看看网络协议是否设置正确 找到"控制面板\所有控制面板项\网络和共享中心&…

通讯录(C语言)

通讯录 一、基本思路及功能介绍二、功能实现1.基础菜单的实现2.添加联系人信息功能实现3.显示联系人信息功能实现4.删除联系人信息功能实现5.查找联系人信息功能实现6.修改联系人信息功能实现7.排序联系人信息功能实现8.加载和保存联系人信息功能实现 三、源文件展示1.test.c2.…

MATLAB图论合集(三)Dijkstra算法计算最短路径

本贴介绍最短路径的计算&#xff0c;实现方式为迪杰斯特拉算法&#xff1b;对于弗洛伊德算法&#xff0c;区别在于计算了所有结点之间的最短路径&#xff0c;考虑到MATLAB计算的便捷性&#xff0c;计算时只需要反复使用迪杰斯特拉即可&#xff0c;暂不介绍弗洛伊德的实现&#…

ChatGPT 与前端技术实现制作大屏可视化

像这样的综合案例实分析,我们可以提供案例,维度与指标数据,让ChatGPT与AIGC 帮写出完整代码,并进行一个2行2列的布局设置。 数据与指令如下: 商品名称 销量 目标 完成率 可乐 479 600 79.83% 雪碧 324 600 54.00% 红茶 379 600 63.…

Unity报错DllNotFoundException:sqlite3

Unity项目中要使用轻型数据库sqlite&#xff0c;除了导入sqlite3.dll外&#xff0c;还需要导入Mono.Data.Sqlite.dll和System.Data.dll&#xff08;工程里或者编辑器里面有System.Data.dll时就不需要&#xff09;两个文件。 如果在编辑器中运行出现 “DllNotFoundException:sql…

优化器调整策略

损失函数的作用是衡量模型输出与真实标签的差异。当我们有了这个loss之后&#xff0c;我们就可以通过反向传播机制得到参数的梯度&#xff0c;那么我们如何利用这个梯度进行更新参数使得模型的loss逐渐的降低呢&#xff1f; 优化器的作用 Pytorch的优化器&#xff1a; 管理并…

nacos总结1

5.Nacos注册中心 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心&#xff0c;SpringCloudAlibaba也推出了一个名为Nacos的注册中心。 5.1.认识和安装Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c…

windows可视化界面管理服务器上的env文件

需求&#xff1a;在 Windows 环境中通过可视化界面编辑位于 Linux 主机上的 env 文件的情况&#xff0c;我现在环境是windows环境&#xff0c;我的env文件在linux的192.168.20.124上&#xff0c;用户是op&#xff0c;密码是op&#xff0c;文件绝对路径是/home/op/compose/env …

CTFhub-sqli注入-报错注入

用到的函数 updatexml(1&#xff0c; &#xff0c;1) concat(0x7e, ,0x7e) group_concat(目标值) right(&#xff0c;32) 1 1 1 union select updatexml(1,concat(0x7e,database(),0x7e),1) 1 union select updatexml(1,concat(0x7e,(select(group_concat(ta…

TensorBoard的使用

TensorBoard&#xff1a;对图像进行变换 1. SummaryWriter的使用 ctrl类出现注释解析&#xff1a; 将条目直接log_dir写入要成为由TensorBoard使用。 “摘要编写器”类提供了一个高级 API 来创建事件文件&#xff0c;并在给定目录中添加摘要和事件。该类更新文件内容异步。…

leetcode.105 从前序和中序遍历序列构造二叉树

题目描述&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一 棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 题目要求&#xff1a; 1 < preorder.length < 3000inorder.length…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 术语解释摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传…

Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2) A ~ D

比赛链接 A 正常枚举就行&#xff0c;从最后一位往前枚举&#xff0c;-1、-2、-3...这样 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing namespace std;typedef pair<int, int> PII; typedef long l…

使用windeployqt和InstallShield打包发布Qt软件的流程

前言 Qt编译之后需要打包发布&#xff0c;并且发布给用户后需要增加一个安装软件&#xff0c;通过安装软件可以实现Qt软件的安装&#xff1b;用于安装软件的软件有很多&#xff0c;这里主要介绍InstallShield使用的流程&#xff1b; 使用windeployqt打包Qt编译后的程序 Qt程序…

手写数字识别之损失函数

目录 交叉熵 手写数字识别之损失函数 分类任务的损失函数 Softmax函数 交叉熵的简单理解&#xff1a;真实分布与非真实分布的交叉&#xff0c;完全对应&#xff0c;熵为0 交叉熵的代码实现 交叉熵 给定一个策略, 交叉熵就是在该策略下猜中颜色所需要的问题的期望值。更普…

LeetCode-56-合并区间

题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 可以使用 LinkedList&#xff0c;…