C语言几种常见状态机
·
- 枚举 + 函数指针法
这种方法通过 enum 来定义所有可能的状态,每个状态对应一个处理函数。状态切换是通过函数指针数组来完成的。这种方法适合较为复杂的状态机,状态间的行为是相互独立的,可以灵活扩展。
优点:
代码清晰,容易扩展。
独立的状态函数使得每个状态的行为和处理逻辑都很直观。
实现示例:
#include <stdio.h>
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED,
STATE_MAX
} State;
// 状态处理函数
void state_idle(void) {
printf("Device is in Idle state.\n");
}
void state_running(void) {
printf("Device is in Running state.\n");
}
void state_stopped(void) {
printf("Device is in Stopped state.\n");
}
// 函数指针数组,用来映射状态到对应的处理函数
void (*state_func[STATE_MAX])(void) = {state_idle, state_running, state_stopped};
int main() {
State current_state = STATE_IDLE;
// 模拟状态机运行
while (1) {
state_func[current_state](); // 调用当前状态的函数
current_state = (current_state + 1) % STATE_MAX; // 循环状态
}
return 0;
}
- 状态转换表
状态转换表方法使用一个二维数组,其中行表示当前状态,列表示触发事件。表中的值表示状态转移后的状态。通过查找表来决定下一个状态。这种方式适合事件驱动的状态机,通常用于状态和事件之间有明确映射的场景。
优点:
适用于状态和事件之间的规则简单且固定的场景。
代码结构紧凑,状态和事件的映射清晰。
实现示例:
#include <stdio.h>
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED,
STATE_MAX
} State;
typedef enum {
EVENT_START,
EVENT_STOP,
EVENT_PAUSE,
EVENT_MAX
} Event;
// 状态转换表:行是当前状态,列是事件
State state_transition[STATE_MAX][EVENT_MAX] = {
{STATE_RUNNING, STATE_IDLE, STATE_IDLE}, // STATE_IDLE
{STATE_RUNNING, STATE_STOPPED, STATE_IDLE}, // STATE_RUNNING
{STATE_IDLE, STATE_IDLE, STATE_IDLE} // STATE_STOPPED
};
void handle_event(State *current_state, Event event) {
*current_state = state_transition[*current_state][event];
switch (*current_state) {
case STATE_IDLE: printf("Idle State\n"); break;
case STATE_RUNNING: printf("Running State\n"); break;
case STATE_STOPPED: printf("Stopped State\n"); break;
}
}
int main() {
State current_state = STATE_IDLE;
handle_event(¤t_state, EVENT_START); // 触发开始事件
handle_event(¤t_state, EVENT_STOP); // 触发停止事件
handle_event(¤t_state, EVENT_PAUSE); // 触发暂停事件
return 0;
}
- 状态机结构体法
这种方法通过定义一个结构体来封装每个状态的信息,每个状态包含进入、退出和运行时的操作。适合更复杂的状态机,每个状态有自己独立的行为和生命周期管理。
优点:
每个状态的行为是封装的,代码更加模块化。
可以独立管理每个状态的操作,如进入、退出等。
实现示例:
#include <stdio.h>
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED,
STATE_MAX
} State;
typedef struct {
State state;
void (*enter)(void); // 进入状态时的操作
void (*exit)(void); // 离开状态时的操作
void (*run)(void); // 当前状态的执行操作
} StateMachine;
// 状态操作函数
void enter_idle(void) { printf("Entering Idle State\n"); }
void exit_idle(void) { printf("Exiting Idle State\n"); }
void run_idle(void) { printf("Running Idle State\n"); }
void enter_running(void) { printf("Entering Running State\n"); }
void exit_running(void) { printf("Exiting Running State\n"); }
void run_running(void) { printf("Running Running State\n"); }
StateMachine fsm[STATE_MAX] = {
{STATE_IDLE, enter_idle, exit_idle, run_idle}, // STATE_IDLE
{STATE_RUNNING, enter_running, exit_running, run_running}, // STATE_RUNNING
{STATE_STOPPED, NULL, NULL, NULL} // STATE_STOPPED
};
int main() {
fsm[STATE_IDLE].enter(); // 进入空闲状态
fsm[STATE_IDLE].run(); // 执行空闲状态操作
fsm[STATE_IDLE].exit(); // 离开空闲状态
fsm[STATE_RUNNING].enter(); // 进入运行状态
fsm[STATE_RUNNING].run(); // 执行运行状态操作
fsm[STATE_RUNNING].exit(); // 离开运行状态
return 0;
}
- 基于 switch 的状态机
这种方法直接使用 switch 语句来实现状态转移。适用于状态和事件较少且逻辑简单的场景。它实现起来简单直接,但对于复杂的状态机,代码可能变得冗长且难以扩展。
优点:
实现简单,适合状态较少的场景。
代码直接,容易理解。
实现示例:
#include <stdio.h>
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_STOPPED
} State;
int main() {
State current_state = STATE_IDLE;
while (1) {
switch (current_state) {
case STATE_IDLE:
printf("Idle State\n");
current_state = STATE_RUNNING; // 状态转换
break;
case STATE_RUNNING:
printf("Running State\n");
current_state = STATE_STOPPED; // 状态转换
break;
case STATE_STOPPED:
printf("Stopped State\n");
current_state = STATE_IDLE; // 状态转换
break;
}
}
return 0;
}
总结:
枚举 + 函数指针法:适合复杂的状态机,每个状态有独立的处理函数,扩展性好。
状态转换表:适用于状态和事件之间的转换规则简单且明确的场景,代码简洁。
状态机结构体法:适合需要封装更多信息的状态机,每个状态可以有独立的进入、退出、执行操作。
switch 语句法:适合状态较少且逻辑简单的场景,快速实现。
选择哪种方式取决于你的需求。如果需要更复杂的行为或状态间的转换逻辑,可以考虑使用枚举 + 函数指针法或状态机结构体法;如果状态和事件比较简单,可以使用状态转换表或 switch 语句法。
更多推荐



所有评论(0)