状态机的实现方法--C语言版本
状态机(State Machine)用于表示有限个状态以及在这些状态之间的转移和动作。在计算机科学中,状态机常用于建模程序的行为,尤其是那些需要根据输入或事件改变行为的系统。
状态机的实现方式有多种,常见的有:
-
使用switch-case语句
-
使用状态表(State Table)
-
使用函数指针(State Function Pointer)
1. 使用switch-case语句
这种方式是最直接的状态机实现方法。通过一个循环和switch语句,根据当前状态和输入事件执行相应的操作,并转换状态。
示例:一个简单的状态机,有两个状态:STATE_A和STATE_B,两个事件:EVENT_1和EVENT_2。
状态转移规则:
-
在STATE_A下,遇到EVENT_1则执行动作A1并转移到STATE_B;遇到EVENT_2则执行动作A2并保持在STATE_A。
-
在STATE_B下,遇到EVENT_1则执行动作B1并保持在STATE_B;遇到EVENT_2则执行动作B2并转移到STATE_A
#include <stdio.h>// 定义状态和事件
typedef enum {STATE_A,STATE_B
} State;typedef enum {EVENT_1,EVENT_2
} Event;// 状态机函数
void state_machine(Event event) {static State current_state = STATE_A;switch (current_state) {case STATE_A:switch (event) {case EVENT_1:printf("在STATE_A处理EVENT_1,执行动作A1,转移到STATE_B\n");current_state = STATE_B;break;case EVENT_2:printf("在STATE_A处理EVENT_2,执行动作A2,保持在STATE_A\n");break;}break;case STATE_B:switch (event) {case EVENT_1:printf("在STATE_B处理EVENT_1,执行动作B1,保持在STATE_B\n");break;case EVENT_2:printf("在STATE_B处理EVENT_2,执行动作B2,转移到STATE_A\n");current_state = STATE_A;break;}break;}
}int main() {// 模拟事件序列state_machine(EVENT_1);state_machine(EVENT_2);state_machine(EVENT_1);state_machine(EVENT_1);return 0;
}
2. 使用状态表(State Table)
状态表是一个二维数组,其中行表示当前状态,列表示事件,每个单元格包含下一个状态和要执行的动作。这种方式适用于状态和事件较多的情况,可以清晰地表示状态转移。
示例:同样实现上面的状态机。
#include <stdio.h>// 定义状态和事件
typedef enum {STATE_A,STATE_B,STATE_COUNT
} State;typedef enum {EVENT_1,EVENT_2,EVENT_COUNT
} Event;// 定义动作函数指针
typedef void (*Action)(void);// 动作函数
void action_A1() { printf("执行动作A1\n"); }
void action_A2() { printf("执行动作A2\n"); }
void action_B1() { printf("执行动作B1\n"); }
void action_B2() { printf("执行动作B2\n"); }// 状态表项,包含下一个状态和要执行的动作
typedef struct {State next_state;Action action;
} Transition;// 状态表
Transition state_table[STATE_COUNT][EVENT_COUNT] = {[STATE_A] = {[EVENT_1] = {STATE_B, action_A1},[EVENT_2] = {STATE_A, action_A2}},[STATE_B] = {[EVENT_1] = {STATE_B, action_B1},[EVENT_2] = {STATE_A, action_B2}}
};// 状态机函数
void state_machine(Event event) {static State current_state = STATE_A;Transition transition = state_table[current_state][event];transition.action();current_state = transition.next_state;
}int main() {state_machine(EVENT_1);state_machine(EVENT_2);state_machine(EVENT_1);state_machine(EVENT_1);return 0;
}
3. 使用函数指针(State Function Pointer)
这种方式将每个状态实现为一个函数,函数内部根据事件决定下一个状态和要执行的动作。状态机维护一个当前状态的函数指针,当事件发生时,调用当前状态对应的函数。
示例:同样实现上面的状态机
#include <stdio.h>// 定义状态和事件
typedef enum {EVENT_1,EVENT_2
} Event;// 前向声明状态函数
typedef void (*StateFunction)(Event);// 状态函数声明
void state_a(Event event);
void state_b(Event event);// 全局状态变量,保存当前状态的函数指针
StateFunction current_state = state_a;// 状态A的处理函数
void state_a(Event event) {switch (event) {case EVENT_1:printf("在STATE_A处理EVENT_1,执行动作A1,转移到STATE_B\n");current_state = state_b;break;case EVENT_2:printf("在STATE_A处理EVENT_2,执行动作A2,保持在STATE_A\n");break;}
}// 状态B的处理函数
void state_b(Event event) {switch (event) {case EVENT_1:printf("在STATE_B处理EVENT_1,执行动作B1,保持在STATE_B\n");break;case EVENT_2:printf("在STATE_B处理EVENT_2,执行动作B2,转移到STATE_A\n");current_state = state_a;break;}
}// 状态机函数
void state_machine(Event event) {current_state(event);
}int main() {state_machine(EVENT_1);state_machine(EVENT_2);state_machine(EVENT_1);state_machine(EVENT_1);return 0;
}
4. 面向对象方式(使用结构体)
#include <stdio.h>
#include <stdlib.h>// 定义状态和事件
typedef enum {STATE_OFF,STATE_ON,STATE_SLEEP
} State;typedef enum {EVENT_POWER_ON,EVENT_POWER_OFF,EVENT_SLEEP,EVENT_WAKE_UP
} Event;// 状态机结构体
typedef struct {State current_state;void (*on_enter)(void);void (*on_exit)(void);
} StateMachine;// 状态进入和退出函数
void enter_off(void) { printf("进入关闭状态\n"); }
void exit_off(void) { printf("退出关闭状态\n"); }void enter_on(void) { printf("进入开启状态\n"); }
void exit_on(void) { printf("退出开启状态\n"); }void enter_sleep(void) { printf("进入睡眠状态\n"); }
void exit_sleep(void) { printf("退出睡眠状态\n"); }// 状态转移表
typedef struct {State current;Event event;State next;
} Transition;Transition transitions[] = {{STATE_OFF, EVENT_POWER_ON, STATE_ON},{STATE_ON, EVENT_POWER_OFF, STATE_OFF},{STATE_ON, EVENT_SLEEP, STATE_SLEEP},{STATE_SLEEP, EVENT_WAKE_UP, STATE_ON},{STATE_SLEEP, EVENT_POWER_OFF, STATE_OFF}
};#define TRANSITION_COUNT (sizeof(transitions) / sizeof(transitions[0]))// 状态机处理函数
void handle_event(StateMachine *sm, Event event) {for (int i = 0; i < TRANSITION_COUNT; i++) {if (transitions[i].current == sm->current_state && transitions[i].event == event) {// 执行退出当前状态if (sm->on_exit) sm->on_exit();// 更新状态sm->current_state = transitions[i].next;// 执行进入新状态if (sm->on_enter) sm->on_enter();return;}}printf("无效的状态转移: 状态=%d, 事件=%d\n", sm->current_state, event);
}int main() {StateMachine sm = {.current_state = STATE_OFF,.on_enter = enter_off,.on_exit = NULL};handle_event(&sm, EVENT_POWER_ON);handle_event(&sm, EVENT_SLEEP);handle_event(&sm, EVENT_WAKE_UP);handle_event(&sm, EVENT_POWER_OFF);return 0;
}
总结
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 嵌套switch | 简单直观,易于理解 | 状态多时代码冗长,维护困难 | 简单状态机 |
| 状态表 | 结构清晰,易于扩展 | 需要额外的表结构,稍复杂 | 复杂状态机 |
| 函数指针 | 灵活,状态逻辑封装好 | 状态间耦合可能较高 | 中等复杂度 |
| 面向对象 | 结构清晰,易于维护 | C语言实现稍繁琐 | 复杂系统 |
选择哪种方式取决于状态机的复杂度、可维护性需求和性能要求。对于简单状态机,嵌套switch足够;对于复杂系统,推荐使用状态表或面向对象方式。
