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

Posted QWQ_DIODA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了状态机编程思维学习笔记(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

以上是关于状态机编程思维学习笔记(C语言)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

改变嵌软开发思维方式之:状态机的三种实现方法

C语言进阶学习笔记一数据的存储(总结篇+思维导图+浮点型部分内容)

C语言学习思路笔记

C语言学习思路笔记