C語言實(shí)現(xiàn)簡單狀態(tài)機(jī)

有限狀態(tài)機(jī)(finite state machine)簡稱FSM付呕,表示有限個狀態(tài)及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學(xué)模型计福,在計算機(jī)領(lǐng)域有著廣泛的應(yīng)用。FSM是一種邏輯單元內(nèi)部的一種高效編程方法徽职,在服務(wù)器編程中象颖,服務(wù)器可以根據(jù)不同狀態(tài)或者消息類型進(jìn)行相應(yīng)的處理邏輯,使得程序邏輯清晰易懂姆钉。
狀態(tài)機(jī)實(shí)現(xiàn)的方式有多種力麸,下面講述三種.

1.使用if/else if語句實(shí)現(xiàn)的FSM

使用if/else if語句是實(shí)現(xiàn)的FSM最簡單最易懂的方法,我們只需要通過大量的if /else if語句來判斷狀態(tài)值來執(zhí)行相應(yīng)的邏輯處理育韩。
看看下面的例子:

#include <stdio.h>
enum year_state
{
    SPRING,
    SUMMER,
    AUTUMN,
    WINTER
};

void spring_thing()
{
    printf("hello spring\n");
}
void summer_thing()
{
    printf("hello summer\n");
}
void autumn_thing()
{
    printf("hello autumn\n");
}
void winter_thing()
{
    printf("hello winter\n");
}

int main()
{
    int state = SPRING;
    while (1)
    {
        if (state == SPRING)
        {
            spring_thing();//相應(yīng)的處理
            state = SUMMER;//狀態(tài)改變
        }
        else if (state == SUMMER)
        {
            summer_thing();
            state = AUTUMN;
        }
        else if (state == AUTUMN)
        {
            autumn_thing();
            state = WINTER;
        }
        else if (state == WINTER)
        {
            winter_thing();
            state = SPRING;
        }
        sleep(1);
    }

    return 0;
}

簡單易懂,這里實(shí)現(xiàn)了四季的更替,因?yàn)橹挥兴姆N狀態(tài),所以邏輯清楚,試想如果有個幾十種狀態(tài),我們的if else將會變得十分之長,維護(hù)起來很麻煩,刪減和添加狀態(tài)變得不方便.但是通過這個例子我們認(rèn)識到狀態(tài)機(jī)的內(nèi)涵.
如下圖:



在狀態(tài)1時,遇到一個事件,此刻發(fā)生狀態(tài)轉(zhuǎn)換,一般在狀態(tài)轉(zhuǎn)換前,先要進(jìn)行事件的處理,然后改變狀態(tài)位.然后進(jìn)入狀態(tài)2,以此類推.

2.使用switch case

這種做法和if else類似,結(jié)構(gòu)上更清楚一些,代碼如下:


int main()
{
    int state = SPRING;
    while (1)
    {
        switch(state){
        case SPRING:
            spring_thing();
            state = SUMMER;
            break;
        case SUMMER:
            summer_thing();
            state = AUTUMN;
            break;
        case AUTUMN:
            autumn_thing();
            state = WINTER;
            break;
        case WINTER:
            winter_thing();
            state = SPRING;
            break;
        default:
            break;

        }
        sleep(1);
    }

    return 0;
}

3.函數(shù)指針實(shí)現(xiàn)FSM

使用函數(shù)指針實(shí)現(xiàn)FSM的思路:建立相應(yīng)的狀態(tài)表和動作查詢表,根據(jù)狀態(tài)表闺鲸、事件筋讨、動作表定位相應(yīng)的動作處理函數(shù),執(zhí)行完成后再進(jìn)行狀態(tài)的切換摸恍。

當(dāng)然使用函數(shù)指針實(shí)現(xiàn)的FSM的過程還是比較費(fèi)時費(fèi)力悉罕,但是這一切都是值得的,因?yàn)楫?dāng)你的程序規(guī)模大時候立镶,基于這種表結(jié)構(gòu)的狀態(tài)機(jī)壁袄,維護(hù)程序起來也是得心應(yīng)手。

首先我們畫出這個表



代碼關(guān)鍵部分:


1.定義狀態(tài)數(shù)據(jù)的枚舉類型

enum year_state
{
    SPRING = 1,
    SUMMER,
    AUTUMN,
    WINTER
};

2.定義事件的枚舉類型

enum year_event
{
    EVENT1 = 1,
    EVENT2,
    EVENT3,
    EVENT4,
};

3.定義狀態(tài)表的數(shù)據(jù)類型

typedef struct FsmTable_s
{
    int event;   //事件
    int CurState;  //當(dāng)前狀態(tài)
    void (*eventActFun)();  //函數(shù)指針
    int NextState;  //下一個狀態(tài)
}FsmTable_t;

4.定義處理函數(shù)及建立狀態(tài)表

void spring_thing()
{
    printf("this is spring\n");
}
void summer_thing()
{
    printf("this is summer\n");
}
void autumn_thing()
{
    printf("this is autumn\n");
}
void winter_thing()
{
    printf("this is winter\n");
}

FsmTable_t year_table[] =
{
    //{到來的事件媚媒,當(dāng)前的狀態(tài)嗜逻,將要要執(zhí)行的函數(shù),下一個狀態(tài)}
    { EVENT1,  SPRING,    summer_thing,  SUMMER },
    { EVENT2,  SUMMER,    autumn_thing,  AUTUMN },
    { EVENT3,  AUTUMN,    winter_thing,  WINTER },
    { EVENT4,  WINTER,    spring_thing,  SPRING },
    //add your codes here
};

5.狀態(tài)機(jī)類型,及狀態(tài)機(jī)接口函數(shù)

/*狀態(tài)機(jī)類型*/
typedef struct FSM_s{
    int curState;//當(dāng)前狀態(tài)
    FsmTable_t * pFsmTable;//狀態(tài)表
    int size;//表的項(xiàng)數(shù)
}FSM_t;

/*狀態(tài)機(jī)注冊,給它一個狀態(tài)表*/
void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
{
    pFsm->pFsmTable = pTable;
}

/*狀態(tài)遷移*/
void FSM_StateTransfer(FSM_t* pFsm, int state)
{
    pFsm->curState = state;
}

/*事件處理*/
void FSM_EventHandle(FSM_t* pFsm, int event)
{
    FsmTable_t* pActTable = pFsm->pFsmTable;
    void (*eventActFun)() = NULL;  //函數(shù)指針初始化為空
    int NextState;
    int CurState = pFsm->curState;
    int g_max_num = pFsm->size;
    int flag = 0; //標(biāo)識是否滿足條件
    int i;

    /*獲取當(dāng)前動作函數(shù)*/
    for (i = 0; i<g_max_num; i++)
    {
        //當(dāng)且僅當(dāng)當(dāng)前狀態(tài)下來個指定的事件缭召,我才執(zhí)行它
        if (event == pActTable[i].event && CurState == pActTable[i].CurState)
        {
            flag = 1;
            eventActFun = pActTable[i].eventActFun;
            NextState = pActTable[i].NextState;
            break;
        }
    }


    if (flag) //如果滿足條件了
    {
        /*動作執(zhí)行*/
        if (eventActFun)
        {
            eventActFun();
        }

        //跳轉(zhuǎn)到下一個狀態(tài)
        FSM_StateTransfer(pFsm, NextState);
    }
    else
    {
        printf("there is no match\n");
    }
}

測試程序代碼為:

/*state.c*/
#include <stdio.h>


enum year_state{
    SPRING = 1,
    SUMMER,
    AUTUMN,
    WINTER
};



enum year_event{
    EVENT1 = 1,
    EVENT2,
    EVENT3,
    EVENT4,
};


typedef struct FsmTable_s{
    int event;   //事件
    int CurState;  //當(dāng)前狀態(tài)
    void (*eventActFun)();  //函數(shù)指針
    int NextState;  //下一個狀態(tài)
}FsmTable_t;




void spring_thing()
{
    printf("this is spring\n");
}
void summer_thing()
{
    printf("this is summer\n");
}
void autumn_thing()
{
    printf("this is autumn\n");
}
void winter_thing()
{
    printf("this is winter\n");
}


FsmTable_t year_table[] =
{
    //{到來的事件栈顷,當(dāng)前的狀態(tài)逆日,將要要執(zhí)行的函數(shù),下一個狀態(tài)}
    { EVENT1,  SPRING,    summer_thing,  SUMMER },
    { EVENT2,  SUMMER,    autumn_thing,  AUTUMN },
    { EVENT3,  AUTUMN,    winter_thing,  WINTER },
    { EVENT4,  WINTER,    spring_thing,  SPRING },
    //add your codes here
};







typedef struct FSM_s{
    int curState;//當(dāng)前狀態(tài)
    FsmTable_t * pFsmTable;//狀態(tài)表
    int size;//表的項(xiàng)數(shù)
}FSM_t;

/*狀態(tài)機(jī)注冊,給它一個狀態(tài)表*/
void FSM_Regist(FSM_t* pFsm, FsmTable_t* pTable)
{
    pFsm->pFsmTable = pTable;
}

/*狀態(tài)遷移*/
void FSM_StateTransfer(FSM_t* pFsm, int state)
{
    pFsm->curState = state;
}

/*事件處理*/
void FSM_EventHandle(FSM_t* pFsm, int event)
{
    FsmTable_t* pActTable = pFsm->pFsmTable;
    void (*eventActFun)() = NULL;  //函數(shù)指針初始化為空
    int NextState;
    int CurState = pFsm->curState;
    int g_max_num = pFsm->size;
    int flag = 0; //標(biāo)識是否滿足條件
    int i;

    /*獲取當(dāng)前動作函數(shù)*/
    for (i = 0; i<g_max_num; i++)
    {
        //當(dāng)且僅當(dāng)當(dāng)前狀態(tài)下來個指定的事件萄凤,我才執(zhí)行它
        if (event == pActTable[i].event && CurState == pActTable[i].CurState)
        {
            flag = 1;
            eventActFun = pActTable[i].eventActFun;
            NextState = pActTable[i].NextState;
            break;
        }
    }


    if (flag) //如果滿足條件了
    {
        /*動作執(zhí)行*/
        if (eventActFun)
        {
            eventActFun();
        }

        //跳轉(zhuǎn)到下一個狀態(tài)
        FSM_StateTransfer(pFsm, NextState);
    }
    else
    {
        printf("there is no match\n");
    }
}


int main()
{
    FSM_t year_fsm;
    FSM_Regist(&year_fsm,year_table);
    year_fsm.curState = SPRING;
    year_fsm.size = sizeof(year_table)/sizeof(FsmTable_t);


    printf("\n-------1--init spring------\n");
    printf("state:%d\n",year_fsm.curState);

    printf("\n-------2--spring->summer------\n");
    FSM_EventHandle(&year_fsm,EVENT1);
    printf("state:%d\n",year_fsm.curState);

    printf("\n-------3--summer->autumn------\n");
    FSM_EventHandle(&year_fsm,EVENT2);
    printf("state:%d\n",year_fsm.curState);

    printf("\n-------4--autumn->winter------\n");
    FSM_EventHandle(&year_fsm,EVENT3);
    printf("state:%d\n",year_fsm.curState);

    printf("\n-------5--winter->spring------\n");
    FSM_EventHandle(&year_fsm,EVENT4);
    printf("state:%d\n",year_fsm.curState);

    printf("\n-------6--receive EVENT2 not EVENT1------\n");
    FSM_EventHandle(&year_fsm,EVENT2);
    printf("state:%d\n",year_fsm.curState);

    return 0;
}

結(jié)果為:

-------1--init spring------
state:1

-------2--spring->summer------
this is summer
state:2

-------3--summer->autumn------
this is autumn
state:3

-------4--autumn->winter------
this is winter
state:4

-------5--winter->spring------
this is spring
state:1

-------6--receive EVENT2 not EVENT1------
there is no match
state:1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末室抽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子靡努,更是在濱河造成了極大的恐慌坪圾,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惑朦,死亡現(xiàn)場離奇詭異兽泄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)行嗤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門已日,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人栅屏,你說我怎么就攤上這事飘千。” “怎么了栈雳?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵护奈,是天一觀的道長。 經(jīng)常有香客問我哥纫,道長霉旗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任蛀骇,我火速辦了婚禮厌秒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘擅憔。我一直安慰自己鸵闪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布暑诸。 她就那樣靜靜地躺著蚌讼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪个榕。 梳的紋絲不亂的頭發(fā)上篡石,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機(jī)與錄音西采,去河邊找鬼凰萨。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沟蔑。 我是一名探鬼主播湿诊,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘦材!你這毒婦竟也來了厅须?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤食棕,失蹤者是張志新(化名)和其女友劉穎朗和,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體簿晓,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眶拉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了憔儿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忆植。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谒臼,靈堂內(nèi)的尸體忽然破棺而出朝刊,到底是詐尸還是另有隱情,我是刑警寧澤蜈缤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布拾氓,位于F島的核電站,受9級特大地震影響底哥,放射性物質(zhì)發(fā)生泄漏咙鞍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一趾徽、第九天 我趴在偏房一處隱蔽的房頂上張望续滋。 院中可真熱鬧,春花似錦孵奶、人聲如沸吃粒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至事示,卻和暖如春早像,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肖爵。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工卢鹦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓冀自,卻偏偏與公主長得像揉稚,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子熬粗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,831評論 0 38
  • 『代碼github地址』 標(biāo)簽: 有限狀態(tài)機(jī),Akka fsm,squirrel-foundation搀玖,java狀...
    醉叁重閱讀 29,951評論 3 24
  • C++是一門非常適合用來構(gòu)建DSL(Domain Specific Language)的語言,它的多范式特點(diǎn)為它提...
    MagicBowen閱讀 8,364評論 5 14
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法驻呐,類相關(guān)的語法灌诅,內(nèi)部類的語法,繼承相關(guān)的語法含末,異常的語法猜拾,線程的語...
    子非魚_t_閱讀 31,660評論 18 399
  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運(yùn)用指針編程是C語言最主要的風(fēng)格之一佣盒。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu)挎袜; ...
    朱森閱讀 3,449評論 3 44