之前寫過一篇狀態機的代碼實用文章,很多朋友說有幾個地方有點難度不易理解,函數今天給大家換種簡單寫法,指針狀態使用函數指針的實現方法實現狀態機。
狀態機簡介
有限狀態機FSM是簡單機附有限個狀態及在這些狀態之間的轉移和動作等行為的數學模型,是代碼一種邏輯單元內部的高效編程方法,可以根據不同狀態或者消息類型進行相應的函數處理邏輯,使得程序邏輯清晰易懂。指針狀態
函數指針實現FSM
使用函數指針實現FSM可以分為3個步驟
建立相應的實現狀態表和動作查詢表
根據狀態表、事件、簡單機附動作表定位相應的代碼動作處理函數
執行完成后再進行狀態的切換
代碼實現步驟
定義狀態數據的枚舉類型
typedef?enum?{
??state_1=1,
??state_2,
??state_3,
??state_4
}State;
定義事件的枚舉類型
typedef?enum{
??event_1=1,
??event_2,
??event_3,
??event_4,
??event_5
}EventID;
定義狀態表的數據類型
typedef?struct
{
????int?event;???//事件
????int?CurState;??//當前狀態
????void?(*eventActFun)();??//函數指針
????int?NextState;??//下一個狀態
}StateTable;
定義處理函數及建立狀態表
void?f121()
{
????printf("this?is?f121\n");
}
void?f221()
{
????printf("this?is?f221\n");
}
void?f321()
{
????printf("this?is?f321\n");
}
void?f122()
{
????printf("this?is?f122\n");
}
StateTable?fTable[]?=
{
????//{ 到來的事件,當前的狀態,將要要執行的函數,下一個狀態}
????{ ?event_1,??state_1,????f121,??event_2?},
????{ ?event_2,??state_2,????f221,??event_3?},
????{ ?event_3,??state_3,????f321,??event_4?},
????{ ?event_4,??state_4,????f122,??event_1?},
????//add?your?code?here
};
狀態機類型,及狀態機接口函數
/*狀態機類型*/
typedef?struct?{
????int?curState;//當前狀態
????StateTable?*?stateTable;//狀態表
????int?size;//表的項數
}fsmType;
/*狀態機注冊,給它一個狀態表*/
void?fsmRegist(fsmType*?pFsm,?StateTable*?pTable)
{
????pFsm->stateTable?=?pTable;
}
/*狀態遷移*/
void?fsmStateTransfer(fsmType*?pFsm,?int?state)
{
????pFsm->curState?=?state;
}
/*事件處理*/
void?fsmEventHandle(fsmType*?pFsm,?int?event)
{
????StateTable*?pActTable?=?pFsm->stateTable;
????void?(*eventActFun)()?=?NULL;??//函數指針初始化為空
????int?NextState;
????int?CurState?=?pFsm->curState;
????int?maxNum?=?pFsm->size;
????int?flag?=?0;?//標識是否滿足條件
????/*獲取當前動作函數*/
????for?(int?i?=?0;?i????{
????????//當且僅當當前狀態下來個指定的事件,我才執行它
????????if?(event?==?pActTable[i].event?&&?CurState?==?pActTable[i].CurState)
????????{
????????????flag?=?1;
????????????eventActFun?=?pActTable[i].eventActFun;
????????????NextState?=?pActTable[i].NextState;
????????????break;
????????}
????}
????if?(flag)?//如果滿足條件了
????{
????????/*動作執行*/
????????if?(eventActFun)
????????{
????????????eventActFun();
????????}
????????//跳轉到下一個狀態
????????fsmStateTransfer(pFsm,?NextState);
????}
????else
????{
????????printf("there?is?no?match\n");
????}
}
附代碼
代碼直接復制過去就行啦,本想打包的,太麻煩了。
測試程序
//編譯器:http://www.dooccn.com/cpp/
//來源:技術讓夢想更偉大
//作者:李肖遙
#include?
typedef?enum?{
??state_1=1,
??state_2,
??state_3,
??state_4
}State;
typedef?enum{
??event_1=1,
??event_2,
??event_3,
??event_4,
??event_5
}EventID;
typedef?struct?{
????int?event;???//事件
????int?CurState;??//當前狀態
????void?(*eventActFun)();??//函數指針
????int?NextState;??//下一個狀態
}StateTable;
void?f121()
{
????printf("this?is?f121\n");
}
void?f221()
{
????printf("this?is?f221\n");
}
void?f321()
{
????printf("this?is?f321\n");
}
void?f122()
{
????printf("this?is?f122\n");
}
StateTable?fTable[]?=
{
????//{ 到來的事件,當前的狀態,將要要執行的函數,下一個狀態}
????{ ?event_1,??state_1,????f121,??event_2?},
????{ ?event_2,??state_2,????f221,??event_3?},
????{ ?event_3,??state_3,????f321,??event_4?},
????{ ?event_4,??state_4,????f122,??event_1?},
????//add?your?code?here
};
/*狀態機類型*/
typedef?struct?{
????int?curState;//當前狀態
????StateTable?*?stateTable;//狀態表
????int?size;//表的項數
}fsmType;
/*狀態機注冊,給它一個狀態表*/
void?fsmRegist(fsmType*?pFsm,?StateTable*?pTable)
{
????pFsm->stateTable?=?pTable;
}
/*狀態遷移*/
void?fsmStateTransfer(fsmType*?pFsm,?int?state)
{
????pFsm->curState?=?state;
}
/*事件處理*/
void?fsmEventHandle(fsmType*?pFsm,?int?event)
{
????StateTable*?pActTable?=?pFsm->stateTable;
????void?(*eventActFun)()?=?NULL;??//函數指針初始化為空
????int?NextState;
????int?CurState?=?pFsm->curState;
????int?maxNum?=?pFsm->size;
????int?flag?=?0;?//標識是否滿足條件
????/*獲取當前動作函數*/
????for?(int?i?=?0;?i????{
????????//當且僅當當前狀態下來個指定的事件,我才執行它
????????if?(event?==?pActTable[i].event?&&?CurState?==?pActTable[i].CurState)
????????{
????????????flag?=?1;
????????????eventActFun?=?pActTable[i].eventActFun;
????????????NextState?=?pActTable[i].NextState;
????????????break;
????????}
????}
????if?(flag)?//如果滿足條件了
????{
????????/*動作執行*/
????????if?(eventActFun)
????????{
????????????eventActFun();
????????}
????????//跳轉到下一個狀態
????????fsmStateTransfer(pFsm,?NextState);
????}
????else
????{
????????printf("there?is?no?match\n");
????}
}
int?main()
{
????fsmType?pType;
????fsmRegist(&pType,fTable);
????pType.curState?=?state_1;
????pType.size?=?sizeof(fTable)/sizeof(StateTable);
????printf("init?state:%d\n\n",pType.curState);
????fsmEventHandle(&pType,event_1);
????printf("state:%d\n\n",pType.curState);
????fsmEventHandle(&pType,event_2);
????printf("state:%d\n\n",pType.curState);
????fsmEventHandle(&pType,event_3);
????printf("state:%d\n\n",pType.curState);
????fsmEventHandle(&pType,event_4);
????printf("state:%d\n\n",pType.curState);
????fsmEventHandle(&pType,event_2);
????printf("state:%d\n\n",pType.curState);
????return?0;
}
編譯結果

總結
使用函數指針實現的FSM的過程還是比較費時費力的,但是這一切相對一大堆的if/else、switch/case
來說都是值得的,當你的程序規模變得越來越大的時候,基于這種表結構的狀態機,維護程序起來會清晰很多。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!