从 switch-case 到函数指针表
2026/8/24大约 2 分钟
C语言状态机:从 switch-case 到函数指针表
状态机的核心:
当前状态 + 当前事件 → 决定下一步动作和下一个状态。
例如一个设备:
IDLE → RUNNING → ERROR一、最简单的 switch-case
定义状态:
typedef enum
{
IDLE,
RUNNING,
ERROR
} State;定义事件:
typedef enum
{
START,
FAULT
} Event;状态机:
State state = IDLE;
void fsm_run(Event event)
{
switch (state)
{
case IDLE:
if (event == START)
state = RUNNING;
break;
case RUNNING:
if (event == FAULT)
state = ERROR;
break;
case ERROR:
break;
}
}执行:
fsm_run(START);状态:
IDLE
↓ START
RUNNING再:
fsm_run(FAULT);变成:
RUNNING
↓ FAULT
ERROR这就是最基本的状态机。
二、为什么需要状态机?
因为很多嵌入式程序不是:
收到数据 → 做一次事情而是:
等待
↓
开始
↓
接收
↓
完成
↓
出错例如协议解析:
WAIT_HEADER
↓
WAIT_LEN
↓
WAIT_DATA
↓
WAIT_CRC
↓
DONE不同状态下,同一个事件的处理方式不同。
所以状态机比堆很多 if-else 更清晰。
三、状态多了怎么办?
如果状态越来越多:
switch (state)
{
case STATE_0:
case STATE_1:
case STATE_2:
case STATE_3:
...
}switch 会越来越大。
这时可以把每个状态的处理函数放进函数指针数组。
typedef void (*state_handler)(Event event);
state_handler handlers[] =
{
idle_handler,
running_handler,
error_handler
};然后:
handlers[state](event);直接根据状态找到对应处理函数。
四、状态机就变成
state
↓
handlers[state]
↓
当前状态处理函数
↓
根据 event 决定下一状态例如:
void idle_handler(Event event)
{
if (event == START)
state = RUNNING;
}
void running_handler(Event event)
{
if (event == FAULT)
state = ERROR;
}统一执行:
void fsm_run(Event event)
{
handlers[state](event);
}五、两种方法怎么选?
switch-case
→ 状态少
→ 逻辑简单
→ 最直观
函数指针表
→ 状态多
→ 每个状态逻辑复杂
→ 更容易扩展不要为了“高级”强行使用函数指针。
状态少就
switch,状态多再考虑状态表。
六、嵌入式中的典型场景
协议解析
→ 等待帧头 → 收长度 → 收数据 → CRC
设备控制
→ 初始化 → 工作 → 故障 → 恢复
UI
→ 主界面 → 设置 → 调试 → 返回七、最终记忆
状态机:
当前状态
+
事件
↓
执行当前状态逻辑
↓
决定下一状态最简单:
switch (state)状态复杂:
handlers[state](event);状态机的本质,就是把“程序现在处于什么阶段”明确表示出来,再根据事件决定下一步。

