转载声明 如果转载本博客内容,请联系869119842@qq.com,获得作者书面授权

前言

状态机的好处不用多说,自己百度去,但传统的编程模式,无论是C语言,或是硬件FPGA的Verilog都是采用switch-case结构,硬件的还好说,是并行的,但如果是C语言实现状态机则可能需要对每个case进行判断,状态少比如几个可能没什么效率之类的问题,但状态多几十个上百个呢,那么就需要进行上百次的判断是否匹配,毫无疑问效率很低,切每次的状态切换时间也不确定。那么有没有一种好的实现模式不用switch-case结构呢?下面我来为C语言状态机的实现建立一个最优模式。

实现

大家写状态机常用的是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
请按任意键继续. . .

总结

以这种模式不仅可以实现上面的Moore型状态机,还可根据实际实现Mealy型状态机,结构清晰易懂。

 
 

C语言状态机的更多相关文章

  1. C语言状态机模板

    转载声明:如果转载本博客内容,请联系869119842@qq.com,获得作者书面授权. 前言 上一篇我的博客中探讨了一种非swtich-case结构的状态机写法,但是个人感觉写起来比较麻烦,如果增加 ...

  2. 开发经验之状态机思想,分别使用了swift,OC,C,PHP语言实现

    这里设计一个简单的练习,使用状态机思想实现,分别使用了swift,OC,C,PHP语言实现 题目:1到10000遍历,开始-打印奇数-遇到7的倍数开始打印偶数--遇到10的倍数打印奇数   //部分结 ...

  3. 状态机的c语言编程

    http://blog.csdn.net/shandongdaya/article/details/7282547 一 有限状态机的实现方式 有限状态机(Finite State Machine或者F ...

  4. geek青年的状态机,查表,纯C语言实现

    geek青年的状态机,查表,纯C语言实现 1. 问题的提出.抽象 建一,不止是他,不少人跟我讨论过这种问题:怎样才干保证在需求变更.扩充的情况下.程序的主体部分不动呢? 这是一个很深刻和艰难的问题.在 ...

  5. 普通的年轻状态机,纯C语言

    我们第一次接触到了状态机.在数字电路课程.计数器.串行奇偶校验.考了1连续报错电路 等待,两者都需要一个状态机模型.电路实现这些功能,与状态机的状态转移图.状态转移表是等价. 后.然后,我们联系了状态 ...

  6. [状态机]嵌入式设计模式:有限状态自动机的C语言实现

    转自:http://www.cnblogs.com/autosar/archive/2012/06/22/2558604.html 状态机模式是一种行为模式,在<设计模式>这本书中对其有详 ...

  7. 【编程之美】用C语言实现状态机(实用)

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://www.cnblogs.com/lihuidashen/p/115105 ...

  8. QM UML状态机建模实例之Blinky for cortex-m0

    简介:QP由Quantum Leaps公司开发异于传统顺序式系统(前后台架构即main+ISR)和传统多任务系统(操作系统)的事件驱动型状态机框架,实现了在C语言下的面向对象编程,该框架支持有限状态机 ...

  9. 如何系统地自学一门Python 语言(转)

    转自:http://www.phpxs.com/post/4521 零基础情况下,学一门语言充实下自己,Python,简洁.优美.容易使用,是一个很好的选择.那么如何系统地自学Python呢? 有的人 ...

随机推荐

  1. Centos启动Cassandra交互模式失败:No appropriate python interpreter found

    在CentOS6.5安装好Cassandra后,启动交互模式: bin/sqlsh 192.168.10.154 时,报错 No appropriate python interpreter foun ...

  2. BurpSuite 抓手机包

    Windows and Phone 处于同一无线环境下 Windows   Phone   Burp Suite设置

  3. javac 导入第三方jar包

    如果是导入一个包,只需要 javac -classpath xxx/xxx/xxx.jar xxx.java 即可 如果有多个包,windows下用分号隔开,Lunix下用冒号隔开即可.

  4. 深入springMVC------文件上传源码解析(上篇)

    最近在项目中,使用springmvc 进行上传文件时,出现了一个问题: org.springframework.web.multipart.MultipartException: The curren ...

  5. Windows 10系统更换Windows 7系统磁盘分区注意事项二

    1.在原WIN10系统中将硬盘的GPT分区表格式转换为MBR分区表格式 上一篇关于新机预装WIN10系统更换为WIN7系统中说到需要将硬盘的GPT分区表格式转换为MBR分区表格式,在文章末尾给出的链接 ...

  6. 用ORBSLAM2运行TUM Dataset数据集

    参照https://github.com/raulmur/ORB_SLAM2/blob/master/README.md 运行 4. Monocular Examples TUM Dataset 数据 ...

  7. (转)Tomcat数据源连接池加密

    文章来源 :http://my.oschina.net/cimu/blog/164757 我们在使用Tomcat数据库连接池的时候都是明文存储数据库用户名和密码的,例如: <Resource n ...

  8. shell 转义字符的写法

    在链接中,往往会遇到含有‘&'字符的情形,需要转义方能使用. 以下是它的写法样例,错误的写法勿要再犯!! 链接样例: http://my.example.cn/show/details/htd ...

  9. 6、HTML5表单提交和PHP环境搭建

    ---恢复内容开始--- 1.块元素 块元素在显示的时候,通常会以新行开始 如:<h1> <p> <ul> <!-- 块—>注释 <p>he ...

  10. 【图像处理】【SEED-VPM】6.文件目录结构

    ———————————————————————————————————————————————————————————————————————— seed-vpm6467 \ Hardware Tes ...