# 【C语言最佳实践】状态机
作者:wallace-lai
发布:2022-08-04
更新:2022-08-04
## 一、状态机要解决的问题
状态机所要解决的问题:
(1)计算机最擅长处理重复性的工作
(2)现实世界中大量事物的工作流程取决于其他事物的状态
(3)状态机是针对多种状态之间的迁移而建立的一个简单数学模型
(4)状态机可用来指导硬件模块或者软件模块的设计
状态机的概念:
(1)一个硬件或者一个软件模块
(2)我们给状态机一个初始状态
(3)然后给状态机一个事件
(4)状态机依据事件和当前状态完成一个动作或者转换到另一个状态
(5)终止执行或者等待下一个事件 如此,状态机就成了一个可重复工作的机器
## 二、状态机相关理论
术语:
- 状态(state):可分为当前状态(现态)和迁移后的新状态(次态)
- 事件(Event):又称为条件
- 动作(Action)
- 迁移、转换或者变换
分类:
- Moore状态机:次态和事件无关(极其简单的状态机)
- Mealy状态机:次态和现态及事件有关
## 三、正确理解状态机
所有含有条件分支代码的程序或者函数都可以看成一个状态机
- 简单的事件驱动代码,并不构成状态机
- 状态机里必须存在状态迁移
## 四、状态机的软件实现
### 确定性状态机
(1)有限的状态
(2)有限的输入事件
(3)有确定的状态迁移规则
### 自定义状态机
(1)自定义状态
(2)有限的输入事件
(3)某种确定的状态迁移规则
### 确定性状态机实现步骤
(1)使用状态迁移图/表清理迁移关系
(2)状态数量较少时,一个包含上下文信息的函数搞定,内部使用条件分支代码
(3)状态数量较多时,为每个状态定义回调函数,在回调函数中实现对这个状态的处理
### 状态机举例:判断立即数
(1)前缀`0x`表示十六进制、前缀`0`表示八进制,其他表示十进制
(2)状态:为止、零前缀、八进制、十六进制、十进制、错误
```c
enum {
SSM_UNKNOWN = 0,
SSM_PREFIX_ZERO,
SSM_RESULT_FIRST,
SSM_OCT = SSM_RESULT_FIRST,
SSM_HEX,
SSM_DEC,
SSM_ERR,
};
struct _scale_state_machine {
int state;
};
static int init_scale_state_machine(struct _scale_state_machine *sm)
{
sm->state = SSM_UNKNOWN;
}
static int scale_sm_check_scale(struct _scale_state_machine *sm, char ch)
{
switch (sm->state) {
case SSM_UNKNOWN:
if (ch == '0')
sm->state = SSM_PREFIX_ZERO;
else if (ch >= '1' && ch <= '9') {
sm->state = SSM_DEC;
} else {
sm->state = SSM_ERR;
}
break;
case SSM_PREFIX_ZERO:
if (ch == 'x' || ch == 'X') {
sm->state = SSM_HEX;
} else if (ch >= '0' || ch <= '7') {
sm->state = SSM_OCT;
} else {
sm->state = SSM_ERR;
}
break;
}
return sm->state;
}
int check_scale(const char *literal)
{
struct _scale_state_machine sm;
init_scale_state_machine(&sm);
while (*literal) {
int scale = scale_sm_check_scale(&sm, *literal);
if (scale >= SSM_RESULT_FIRST)
return scale;
literal++;
}
return SSM_ERR;
}
```
### 复杂案例:Lexbor实现HTML规范解析器
略
### 自定义状态机
(1)事件是确定的、有限的
(2)状态是一种抽象对象而不是一个简单的枚举变量;状态被组织为链表或者树形数据结构
(3)每个状态可根据输入事件构造一个抽象的迁移对象
(4)迁移对象实现动作及状态迁移;通常,状态迁移发生在相邻的状态节点之间
(5)状态机构造状态数据结构,并根据状态返回的迁移函数运行,直到停止
### 应用场景
(1)轨迹生成器
- 状态是代表不同轨迹的时间曲线方程(如线性、贝塞尔曲线等)
- 事件是定时器
(2)动画控制器
- 状态是不同的动画效果
- 事件是定时器或者用户输入