78

我只是想知道是否有人知道互联网上有一些用于开发状态机的好教程。还是电子书?

我开始在状态机上工作,只需要一些通用的东西来让我开始。

4

8 回答 8

145

如果您使用函数指针,状态机在 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_transitions()函数,因为它是微不足道的。

这就是我多年来做状态机的方式。

于 2009-09-03T06:06:40.337 回答
30

我更喜欢使用函数指针而不是巨大的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);
}
于 2009-09-05T14:30:27.210 回答
10

不幸的是,大多数关于状态机的文章都是为 C++ 或其他直接支持多态性的语言编写的,因为将 FSM 实现中的状态建模为派生自抽象状态类的类是很好的。

但是,在 C 中使用 switch 语句将事件分派到状态(对于简单的 FSM,它们几乎可以直接编写代码)或使用表将事件映射到状态转换,都非常容易在 C 中实现状态机。

这里有几篇关于 C 语言状态机基本框架的简单但不错的文章:

编辑:网站“维护中”,网络存档链接:

switch基于语句的状态机通常使用一组宏来“隐藏”语句的机制(或switch使用一组if//语句而不是 a ),并使用“FSM 语言”来描述 C 中的状态机来源。我个人更喜欢基于表格的方法,但这些方法当然有优点,被广泛使用,并且特别适用于更简单的 FSM。thenelseswitch

Steve Rabin 在“Game Programming Gems”第 3.0 章(Designing a General Robust AI Engine)中概述了一个这样的框架。

这里讨论了一组类似的宏:

如果您还对 C++ 状态机实现感兴趣,可以找到更多内容。如果您有兴趣,我会发布指针。

于 2009-09-03T07:06:19.420 回答
8

状态机本质上不是需要教程来解释甚至使用的东西。我建议您查看数据以及如何解析它。

例如,我必须解析近太空气球飞行计算机的数据协议,它将数据以特定格式(二进制)存储在 SD 卡上,需要将其解析成逗号分隔的文件。为此使用状态机是最有意义的,因为根据下一位信息是什么,我们需要更改我们正在解析的内容。

该代码使用 C++ 编写,可用作ParseFCU。如您所见,它首先检测我们正在解析的版本,然后从那里进入两个不同的状态机。

它以已知良好的状态进入状态机,此时我们开始解析,根据遇到的字符,我们要么进入下一个状态,要么回到前一个状态。这基本上允许代码自适应数据的存储方式以及某些数据是否存在。

在我的示例中,GPS 字符串不是飞行计算机记录的要求,因此如果找到该单个日志写入的结束字节,则可能会跳过 GPS 字符串的处理。

状态机写起来很简单,总的来说我遵循它应该流动的规则。通过系统的输入应该在一定程度上轻松地从一个州流向另一个州。

于 2009-09-03T05:06:36.337 回答
5

这就是你需要知道的。

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;
    }
}
于 2009-09-03T04:52:01.017 回答
4

实时面向对象建模非常棒(1994 年出版,现在售价仅为 81 美分,外加 3.99 美元的运费)。

于 2009-09-03T04:55:25.563 回答
4

在 C 语言中学习手工制作状态机有很多课程,但我也建议使用 Ragel 状态机编译器:

http://www.complang.org/ragel/

它有非常简单的定义状态机的方法,然后你可以生成图表,生成不同风格的代码(表驱动,goto驱动),如果你想分析代码等等。它很强大,可以在生产中使用各种协议的代码。

于 2009-09-03T08:45:06.810 回答
-6

对于复杂的问题,状态机可能非常复杂。他们也受到意想不到的错误。如果有人遇到错误或将来需要更改逻辑,它们可能会变成一场噩梦。如果没有状态图,它们也很难跟踪和调试。结构化编程要好得多(例如,您可能不会在主线级别使用状态机)。您甚至可以在中断上下文(通常使用状态机的地方)中使用结构化编程。请参阅codeproject.com 上的这篇文章“在中断级别模拟多任务/阻塞代码的宏” 。

于 2016-04-26T01:16:51.743 回答