IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 状态机编程思维学习笔记(C语言) -> 正文阅读

[C++知识库]状态机编程思维学习笔记(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++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-07 13:40:26  更:2021-10-07 13:41:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/29 18:28:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计