状态机教程
我只是想知道是否有人知道在互联网上开发状态机的一些很好的教程。 还是电子书?
我开始在状态机上工作,只需要一些一般的东西让我开始。
如果使用函数指针,状态机在C中非常简单。
基本上你需要2个数组 – 一个用于状态函数指针,另一个用于状态转换规则。 每个状态函数都会返回代码,通过状态查找状态转换表并返回代码来查找下一个状态,然后执行它。
int entry_state(void); int foo_state(void); int bar_state(void); int exit_state(void); /* array and enum below must be in sync! */ int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state}; enum state_codes { entry, foo, bar, end}; enum ret_codes { ok, fail, repeat}; struct transition { enum state_codes src_state; enum ret_codes ret_code; enum state_codes dst_state; }; /* transitions from end state aren't needed */ struct transition state_transitions[] = { {entry, ok, foo}, {entry, fail, end}, {foo, ok, bar}, {foo, fail, end}, {foo, repeat, foo}, {bar, ok, end}, {bar, fail, end}, {bar, repeat, foo}}; #define EXIT_STATE end #define ENTRY_STATE entry int main(int argc, char *argv[]) { enum state_codes cur_state = ENTRY_STATE; enum ret_codes rc; int (* state_fun)(void); for (;;) { state_fun = state[cur_state]; rc = state_fun(); if (EXIT_STATE == cur_state) break; cur_state = lookup_transitions(cur_state, rc); } return EXIT_SUCCESS; }
我不把lookup_transition()
函数,因为它是微不足道的。
这就是我做状态机多年的方式。
我更喜欢在巨大的switch
语句中使用函数指针,但与qrdl的答案相反,我通常不使用显式的返回码或转换表。
而且,在大多数情况下,您需要一个机制来传递额外的数据。 这是一个示例状态机:
#include <stdio.h> struct state; typedef void state_fn(struct state *); struct state { state_fn * next; int i; // data }; state_fn foo, bar; void foo(struct state * state) { printf("%s %i\n", __func__, ++state->i); state->next = bar; } void bar(struct state * state) { printf("%s %i\n", __func__, ++state->i); state->next = state->i < 10 ? foo : 0; } int main(void) { struct state state = { foo, 0 }; while(state.next) state.next(&state); }
状态机并不是本质上需要解释甚至使用教程的东西。 我的build议是,你看看数据,以及如何parsing。
例如,我必须parsingNear Space气球飞行计算机的数据协议,它将数据以特定格式(二进制)存储在SD卡上,需要将其parsing成逗号分隔的文件。 使用状态机是最有意义的,因为取决于下一个信息是什么,我们需要改变我们正在parsing的内容。
代码是使用C ++编写的,可用作ParseFCU 。 正如你所看到的,它首先检测我们正在parsing的版本,并从那里进入两个不同的状态机。
它进入状态机处于已知状态,此时我们开始分析,并根据我们遇到的字符,我们移动到下一个状态,或者回到之前的状态。 这基本上允许代码自适应数据的存储方式,甚至是否存在某些数据。
在我的示例中,GPSstring不是飞行计算机要求login的,所以如果find单个日志写入的结束字节,则可以跳过GPSstring的处理。
状态机很简单,一般来说,我遵循它应该stream的规则。 通过系统的input应该在一定的状态之间轻松stream动。
不幸的是,大多数关于状态机的文章都是为C ++或其他语言编写的,这些语言直接支持多态,因为在FSM实现中将状态build模为派生于抽象状态类的类是很好的。
但是,使用switch语句将事件分派给状态(对于简单的FSM,它们几乎是正确的代码),或者使用表来将事件映射到状态转换,在C中实现状态机非常容易。
在C语言中,有一些关于状态机的基本框架的简单但体面的文章:
- http://www.gedan.net/2008/09/08/finite-state-machine-matrix-style-c-implementation/
- http://www.gedan.net/2009/03/18/finite-state-machine-matrix-style-c-implementation-function-pointers-addon/
编辑 :网站“维护下”,网页档案链接:
- http://web.archive.org/web/20160517005245/http://www.gedan.net/2008/09/08/finite-state-machine-matrix-style-c-implementation
- http://web.archive.org/web/20160808120758/http://www.gedan.net/2009/03/18/finite-state-machine-matrix-style-c-implementation-function-pointers-addon/
基于switch
语句的状态机通常使用一组macros来“隐藏” switch
语句的机制(或者使用一组if
/ then
/ else
语句而不是一个switch
),并使用“FSM语言”在C源代码中描述状态机。 我个人更喜欢基于表格的方法,但这些方法当然有其优点,被广泛使用,并且对于更简单的FSM尤其有效。
Steve Rabin在“Game Programming Gems”第3.0章(devise一个通用的鲁棒AI引擎)中概述了一个这样的框架。
这里讨论一组类似的macros:
如果您也对C ++状态机实现感兴趣,那么可以find更多的东西。 如果你有兴趣,我会发布指针。
实时面向对象的build模是太棒了(1994年出版,现在只卖81美分,加上3.99美元的运费)。
在C中学习手工状态机有很多的经验教训,但是我还build议使用Ragel状态机编译器:
http://www.complang.org/ragel/
它有很简单的定义状态机的方法,然后你可以生成graphics,生成不同风格的代码(表驱动,转到驱动),如果你想分析代码等等,而且function强大,可以用于生产代码为各种协议。
这是你所需要知道的。
int state = 0; while (state < 3) { switch (state) { case 0: // Do State 0 Stuff if (should_go_to_next_state) { state++; } break; case 1: // Do State 1 Stuff if (should_go_back) { state--; } else if (should_go_to_next_state) { state++; } break; case 2: // Do State 2 Stuff if (should_go_back_two) { state -= 2; } else if (should_go_to_next_state) { state++; } break; default: break; } }
状态机对于一个复杂的问题可能非常复杂。 他们也受到意外的错误。 如果有人遇到错误或需要改变未来的逻辑,他们可能变成噩梦。 没有状态图,它们也很难跟踪和debugging。 结构化编程要好得多(例如,你可能不会在主线级使用状态机)。 您甚至可以在中断环境中使用结构化编程(这是通常使用状态机的地方)。 请参阅本文“ codeproject.com ”上的“在中断级别模拟多任务/阻塞代码的macros” 。