状态机编程思维学习笔记(C语言)

前言

不摸鱼摆烂的第一天
请添加图片描述

C语言面对对象特性引入

众所周知,C++是由C语言编写而成,因此,C语言也可以实现一定程度的面对对象编程,接下来为引入状态机的程序,我们先来介绍几个不常用的C语言用法。

函数指针

众所周知,指针是C语言的灵魂,它不仅可以指向各种变量和自己,并且可以指向函数。
先来看正常的C函数

void function_name(void *arg);

这个函数,我们将参数设置为void形式方便后续添加参数。

我们可以根据上方函数声明创建函数指针变量
由于优先级问题,将名称括起来并加上星号

void (*P_function)(void *arg);
/* 注意:前后参数类型必须保持一直 */

这样,在调用时,就可以

P_function = function_name;
/* 函数指针指向对应地址 */

(*P_function)( 填入对应参数 );
/* 调用函数 */

当然,函数的返回值类型可以任意指定,但是上下必须保持一致。

扩展
在上面为什么要使用void* 作为参数呐?
因为如果使用了void*,我们就可以在函数中将它转化为任意类型的变量指针
只需要使用强制类型转换,就可以轻而易举的达到我们的目的。

void function_name(void *arg)
{
	/* int可替换成任意需要的类型 */
    int *need_arg= (int *)arg;
    ...
}

结构体中套用函数指针

前面说了函数指针,如果我们能在结构体中添加函数指针项,就可以实现类似C++一样的面对对象的形式。

/* 定义 */
struct func_struct{
    int n;
    void (*P_func)(int n);
};

/* 使用 */
struct func_struct func;
func->P_func( 100 );

宏定义中##纯替换

宏定义也可以使用一些变量替换,这样方便我们编程,而实现的方法。

#define NUM(n)  (num_##n)
/* n作为变量,##后的n将被替换成参数 */
例如:
NUM(1) 替换为 num_1
NUM(2) 替换为 num_2
NUM(3) 替换为 num_3

状态机概念

要说状态机的概念就得从实例来讲。
比如说一个控制一个发光二极管。
在这里插入图片描述
由图可以知道,二极管有两种状态,亮和灭,分别对应IO口的两种动作,高电平和低电平。从这里出发,可以得出状态机的4个要素

  • 状态:即灯的亮状态和暗状态。
  • 事件:使灯亮的触发条件,比如IO开高电平。
  • 动作:事件发生后执行的动作,这里即为灯亮,但是更为复杂的情况需要一个函数实现,比如向串口发送一个当前状态的报告。
  • 变换:灯亮到灯暗。可以用一个结构体表示所有变换因素。

我个人有个不太恰当的理解,变换作为一个整体的系统(传函)状态就是我们的输出事件就是输入与触发动作自然就是输出状态后的响应

状态机实现

构建一个简单的三层状态机系统
有三个状态,四个事件,并且动作设计为可以现实当前所处状态。

evt_1t2
evt_3t1
evt_2t3
evt_3t1
state1
state2
state3

之后构建状态迁移表
在这里插入图片描述

对状态机的变换结构体,事件和状态进行编写。

#ifndef _STATE_H
#define _STATE_H

typedef enum _sta_{
    state1,state2,state3
}State;

typedef enum _evt_{
    evt_1t2,evt_2t3,evt_3t1,evt_1t3
}Event;

typedef struct {
    State curState; //当前状态
    Event eventId;  //事件ID
    State nextState;//下个状态
    void (*action)(void *arg);//回调函数(动作函数),事件发生后,调用对应的回调函数
}StateTransform;

#endif  /* _STATE_H */

再写出动作函数

void action_callback(void *arg)
{
	/* 将输入参数转化为自己定义的结构体指针类型 */
    StateTransform *statTran = (StateTransform *)arg;
    /* 将转化的内容打印到屏幕上 */
    printf("状态由%d由%d",statTran->curState+1,statTran->nextState+1);
}

构建状态迁移矩阵
此处每个结构体对应数组代表迁移列表中的列。

/*  由于地址上的连续性
	结构体可以直接通过数组进行赋值 */
StateTransform stateTran_1[] = {
        {state1,evt_1t2,state2,action_callback},//每一行代表一个结构体变量
        {state1,evt_2t3,state1,NULL},
        {state1,evt_3t1,state1,NULL},
        {state1,evt_1t3,state3,action_callback},
};

StateTransform stateTran_2[] = {
        {state2,evt_1t2,state2,NULL},
        {state2,evt_2t3,state3,action_callback},
        {state2,evt_3t1,state2,NULL},
        {state2,evt_1t3,state2,NULL},
};

StateTransform stateTran_3[] = {
        {state3,evt_1t2,state3,NULL},
        {state3,evt_2t3,state3,NULL},
        {state3,evt_3t1,state1,action_callback},
        {state3,evt_1t3,state3,NULL},
};

构建完整状态迁移表后,创建一个全局的变量作为当前状态
State GlobleCurState;
之后,进行简单的查表即可。
也就是说,根据当前状态输入的事件在状态迁移表中查询下一个状态和动作响应并执行。

void event_happen(Event event) //输入事件
{
    switch (GlobleCurState) {
        case state1:
            do_action(&STATETRANS(1)[event]); //执行对应动作
            break;
        case state2:
            do_action(&STATETRANS(2)[event]);
            break;
        case state3:
            do_action(&STATETRANS(3)[event]);
            break;
    }
}

void do_action(StateTransform *stateTran)
{
    //状态迁移
    GlobleCurState = stateTran->nextState;
    //调用回调
    if(stateTran->action == NULL)
    {
        printf("Without Action!\n\r");
    }
    else
    {
        stateTran->action((void *)stateTran);
    }
}

最后进行简单测试

#define ENTRY_STATE state1
int main() {
    GlobleCurState = ENTRY_STATE;
    State lastState = GlobleCurState;
    Event User_Event;
    printf("当前状态为state%d\n",GlobleCurState);
    while(1)
    {
        scanf("%d",&User_Event);
        if(User_Event == -1)
            break;
        event_happen(User_Event);
        printf("当前状态为state%d\n",GlobleCurState+1);
    }

    return 0;
}

在这里插入图片描述

参考
什么是状态机?用C语言实现进程5状态模型 作者:一口Linux.
谈谈单片机编程思想——状态机 作者:轻松学C语言.


后文

不得不说,在家太舒服了。。。由于疫情不开学,天天摸鱼,啥也没干,我知道摸鱼不对可是就是停不下来哼哼啊啊啊啊啊啊啊。

但是快开学了,压力在前,就真不能摸了。。。 Orz

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值