C语言状态机学习笔记一

2023-05-16

出处:http://www.cnblogs.com/tangerious/p/4565833.html

状态机的好处不用多说,自己百度去,但传统的编程模式,无论是C语言,或是硬件FPGA的Verilog都是采用switch-case
结构,硬件的还好说,是并行的,但如果是C语言实现状态机则可能需要对每个case进行判断,状态少比如几个可能没什
么效率之类的问题,但状态多几十个上百个呢,那么就需要进行上百次的判断是否匹配,毫无疑问效率很低,切每次的
状态切换时间也不确定。那么有没有一种好的实现模式不用switch-case结构呢?下面我来为C语言状态机的实现建立一
个最优模式。
实现
前面说了一堆装bi的废话,下面进入正题,前段时间研究了下函数式编程,发现C语言的循环结构完全可以用尾递归(不
懂百度)实现,最后C就只剩下唯有if和switch还不能用函数式编程实现,而今天要讲的状态机模式就是用函数式编程实
现switch-case,那么switch-case其实可以看做一种查找的跳转,我们知道C语言中goto可以实现跳转,那么还有什么可
以实现跳转呢,答案是函数调用,但是又需要如何实现指定的函数调用呢,难道要用if判断,显然没什么意义,那么又
没有比if判断(如果上百次判断)更高效的东西呢?有人已经想到是查表,如果将查表和函数调用结合,那么就是函数
指针数组的应用了! 
上代码!首先定义一个函数指针类型,为什么要带void *的参数后面会说:


typedef unsigned char State;
typedef State(*Procedure)(void *);
这样就可以方便地定义一个函数指针数组:


Procedure Steps[] = { step_init, step_count, step_done, step_default };


step_init,step_count等是函数名,再定义状态:


enum states{ s_init, s_count, s_done, s_default };


枚举定义对应着{0,1,2,3},有了这些再状态机联系那么可以想到,数组的索引就是状态定义,上核心代码,两行(简单


吧!关键是想到):


void BestStateMachine(void * invar)
{
    static State NS = s_init; //定义下一状态
    NS = Steps[NS](invar);
}


static的变量NS在每次BestStateMachine调用会得到维护,我们只需再每Steps返回下一个状态并保存到NS中可以实现状


态的保存和切换。再说说为什么要加个void*的参数,状态机一般有很多自身变量的维护,而且对于mealy状态机还需根


据输入判断,因为函数调用返回是不保留局部变量的,那么就需要将变量传递来实现更改和保存,之所以只用了一个


void*参数是因为,如果需要保存和传递的变量很多,直接传递会在调用函数是浪费大量的栈空间,且效率低下,采用这


种模式,你可以将变量用一个结构体封装,然后将结构体指针传递给void *的形参,再函数内部再强制转换即可使用结


构体内部的变量。现在你已经在嘀咕这作者真啰嗦,好上实例代码,就是一个简单的计数器(以前学状态机都从计数器


开始),在计数完成打印信息:
#include<stdio.h>
typedef unsigned char State;
typedef State(*Procedure)(void *);
enum states{ s_init, s_count, s_done, s_default };//状态定义
typedef struct _SM_VAR  //对状态机参数封装
{
    int cnt;
}SM_VAR;
State step_init(void * arg)//初始化
{
    SM_VAR *p = (SM_VAR *)arg;
    p->cnt = 0;
    printf("CS:init ;cnt=%d;NS:count\n", p->cnt);
    return s_count;
}
State step_count(void * arg)//计数
{
    SM_VAR *p = (SM_VAR *)arg;
    if (p->cnt < 3){
        p->cnt+=1;
        printf("CS:count;cnt=%d;NS:count\n", p->cnt);
        return s_count;
    }
    else{
        printf("CS:count;cnt=%d;NS:done\n", p->cnt);
        return s_done;
    }
}
State step_done(void * arg)//计数完成
{
    SM_VAR *p = (SM_VAR *)arg;
    printf("CS:done ;cnt=%d;NS:init\n", p->cnt);
    return s_init;
}
State step_default(void * arg)//错误过程
{
    SM_VAR *p = (SM_VAR *)arg;
    printf("Wrong State\n");
    return s_init;
}
Procedure Steps[] = { step_init, step_count, step_done, step_default };


void BestStateMachine(void * invar)
{
    static State NS = s_init; //定义下一状态
    NS = Steps[NS](invar);
}
int main(void)
{
    SM_VAR var;
    int i;
    for (i = 0; i <8; i++){//给状态机8个周期的时钟驱动
        BestStateMachine(&var);
    }
    return 0;
}


最后在VS2013上调试如下:


CS:init ;cnt=0;NS:count
CS:count;cnt=1;NS:count
CS:count;cnt=2;NS:count
CS:count;cnt=3;NS:count
CS:count;cnt=3;NS:done
CS:done ;cnt=3;NS:init
CS:init ;cnt=0;NS:count
CS:count;cnt=1;NS:count
请按任意键继续. . .

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言状态机学习笔记一 的相关文章

  • 关于物化视图增量刷新报ORA-12018 问题的解决方案

    由于表之前采用的是全量刷新方式进行刷新 xff0c 但是因为表的数据量越来越大 xff0c 全量刷新的时候偶尔会出现失败的情况 xff0c 因为同一个时点刷新的任务比较多 xff0c 回滚段被占满了之后会出现报错 xff0c 所以急需要解决
  • c语言中四种简单的数组排序

    前言 本文介绍了几种c语言中对乱序数组的排序方式 具体的内容有 xff1a 插入排序 xff1b 冒泡排序 xff1b 选择排序 xff1b 希尔排序 xff1b 具体内容详见下文 一 插入排序 1 思路 首先假设数组的的前n位元素是有序的
  • 网桥的功能和分类

    br lan 61 lan 网桥 将WLAN和LAN 交换机 绑定为一个虚拟接口 连接两个局域网 xff0c 负责数据的中继和转发 交换机的前生 集线器 xff08 Hub xff09 是中继器的一种形式 xff0c 区别在于集线器能够提供
  • Java简介

    今天开始学习Java啦 xff0c 每天进步一点点 xff01 1 Java语言发展史 Java语言是美国Sun Stanford University Network 公司在1995年推出的计算机语言 xff0c java之父 xff1a
  • win10上WSL+vscode+xserver配置linux图形化程序开发环境

    受够了双系统来回切换 xff0c 尝试了一下wsl配置linux环境 xff08 个人习惯在linux上敲代码 xff09 xff0c 由于需求图形化 xff0c 又弄了xserver 没有装linux图形界面 WSL 安装按着官方的文档来
  • 02.Ubuntu 18.04安装KVM

    02 Ubuntu 18 04安装KVM 1 检查是否支持虚拟化 span class token function egrep span span class token parameter variable c span span cl
  • maven 打包缺失 resources 目录下的 jar 包,警告 jar should not point at files within the project directory

    报错如下 INFO Scanning for projects WARNING WARNING Some problems were encountered while building the effective model for co
  • Linux 下利用trash替换rm

    前言 rmtrash 是linux和mac下命令行版本rm的回收站 xff0c 安装后对用户透明 xff0c 符合正常使用rm的习惯 支持rm fr file哦 xff0c 有了他再也不怕rm时候手颤抖了 能自动拒绝 rm fr 哦 安装
  • 【异常】记一次前端因资源无法加载导致白屏异常问题

    一 背景 自从运维同事强烈要求前端的环境要使用多套的 xff0c 参考文章 项目 参考若依的前端框架去多环境 于是一番捣鼓与改造之后 xff0c 看似已经顺利了 但运维说 xff0c 前端还是有问题 xff0c 需要他帮我改下 xff0c
  • 三种方法实现后入先出的栈---leetcode题解225

    声明 xff1a 问题描述来源于leetcode 一 问题描述 xff1a 用队列实现栈 CategoryDifficultyLikesDislikesalgorithmsEasy 67 64 480 Tags stack design C
  • iOS|开发小技巧之UIView创建xib

    我们有的时候在创建UIView的时候 xff0c 想要使用xib进行创建视图发现 xff0c xib文件不能和UIView文件一起创建 所以 xff0c 我们要单独创建xib文件 我们选择Empty文件 xff0c 而不要选择View文件
  • PyCharm中安装Vim插件ideavim 并关闭vim编辑模式

    在PyCharm中安装Vim插件ideavim 进入File菜单下的Settings下的Plugins 搜索ideaVim 找到ideaVim插件 点击Install安装 重启并享受在Pycharm环境中使用Vim的乐趣 支持Vim三种模式
  • C++ typedef用法小结 (※不能不看※)

    第一 四个用途 用途一 xff1a 定义一种类型的别名 xff0c 而不只是简单的宏替换 可以用作同时声明指针型的多个对象 比如 xff1a char pa pb 这多数不符合我们的意图 xff0c 它只声明了一个指向字符变量的指针 xff
  • 中兴F450电信光猫改桥接模式

    前几天突然想搞外网访问 xff0c 但是电信这款光猫DMZ不能用让我很愁 xff0c 后来经过一番了解可以让光猫只负责光数转换 xff0c 剩下的事情交给路由 xff0c 但是要把光猫设置成桥接模式 这个光猫比较特殊不需要进入超级管理员只需
  • 群辉默认DDNS功能解析阿里云-自定义服务商

    前言 前不久买了个群辉NAS发现群辉DDNS不能解析阿里云 xff0c 后来找了很多教程都是部署Docker或使用其他平台转发一下 xff0c 然而这些平台还要注册 xff0c 我就在想我自己可不可以实现不需要注册就可以使用的DDNS xf
  • Debian 给非 ROOT 用户添加 sudoer 权限

    问题描述 从官方镜像安装的 Debian 9 xff08 Stretch xff09 比较纯净 xff0c 但因此需要自己安装 配置许多常用的 Linux 应用 xff0c 这里就需要 sudo xff08 super user do xf
  • ffmpeg Could not find codec parameters for stream

    在arm上使用ffmpeg rtmp拉流时出现了下面异常 xff1a flv 64 0x1b0e120 Could not find codec parameters for stream 2 Video h264 none 2560 kb
  • 五、Shell自动化脚本

    一键安装Nginx 脚本 install nginx sh span class token shebang important bin bash span span class token comment Use 使用Shell脚本一键安
  • Mysql数据库备份(一)------数据库备份和表备份

    一 Mysql中的数据备份 Mysql中数据备份使用的命令是 mysqldump 命令将数据库中的数据备份成一个文本文件 表的结构和表中的数据将存储在生成的文本文件中 mysqldump命令的 工作原理很简单 它先查出需要备份的表的结构 x

随机推荐