天天看點

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)

關于狀态機,基礎的知識點可以自行了解,講解的很多,這裡主要是想寫一個有限狀态機FSM通用的寫法,目的在于更好了解,移植,節省代碼閱讀與調試時間,展現出程式設計之美。
傳統的實作方案
  • if...else : 搞一大堆if else, 一個函數寫很長很長......
  • swich...case : 也搞一大堆一個函數寫很長很長......

    先來看看最近做的一個項目,無線通信協定實作的狀态機是什麼樣子的:

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)

    有三種類型的事件:上層下達的指令事件;下層到達的标志和資料傳輸事件;逾時定時器逾時事件。有10種狀态,關聯性很大,複雜了吧,這要是各種if/else的要寫到什麼時候呢。

    偷偷放一張讨論的圖,亂七八糟形容很恰當。

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)

    在事件中判斷狀态,在狀态中判斷事件,橫豎兩種寫法的代碼都比較冗長,看起來呢也不大好,一旦增減,就又要動腦子重新梳理一遍,很累的。

    怎麼去寫呢?其狀态機原理:在根據目前狀态(cur_state) 下,發生事件(event)後,轉移到下一個狀态号(nxt_state),決定執行的動作(action)。盜用一個圖吧

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)

    這裡我們首先定義一個結構體如下:

typedef struct {  State curState;//目前狀态  EventID eventId;//事件ID  State nextState;//下個狀态  Action action;//具體表現}StateTransform;
           

    我們假設有3種狀态,這裡可以随意增加,狀态枚舉如下:

typedef enum {  state_1=1,  state_2,  state_3}State;
           

    我們假設有5個事件,也可以随意增加,事件ID枚舉如下:

typedef enum{  event_1=1,  event_2,  event_3,  event_4,  event_5}EventID;
           

    将其封裝起來在StateMachine中:

typedef struct{  State state;  int transNum;  StateTransform* transform;}StateMachine;
           

    具體流程:目前狀态-有事件觸發-跳到下個狀态-具體表現,重構代碼

StateTransform* findTranss(StateMachine* pSM,  const EventID evt){  int i;  for (i = 0; i < pSM->transNum; i++) {    if ((pSM->transform[i].curState == pSM->state) && (pSM->transform[i].eventId == evt)) {      return &pSM->transform[i];    }  }  return NULL;}
           

    狀态機實作如下:

void runStateMachine(StateMachine* pSM, EventID evt) {  StateTransform* pTrans;  pTrans = findTranss(pSM, evt);  if (pTrans == NULL)  {    xil_printf( "CurState= %s Do not process enent: %s\r\n", pSM->state,evt);    return;  }  pSM->state = pTrans->nextState;  Action act = pTrans->action;  if (act == NULL) {    xil_printf( "change state to %s. No action\r\n",pSM->state);    return;  }  act(&evt);}
           

    最後我模拟一些随機事件,我們隻需要弄清楚事件ID,狀态切換,具體表現就可以了,在代碼中就是填寫 stateTran[] 這個表,一旦有增減事件,狀态等等,也不需要再去使用switch/case,特費腦,其代碼如下:

int run(){  StateMachine stateMachine;  stateMachine.state = state_1;  stateMachine.transNum = 7;  StateTransform stateTran[] = {    {state_1,event_3,state_2,f121},    {state_1,event_4,state_2,NULL},    {state_2,event_1,state_3,f231},    {state_2,event_4,state_2,f221},    {state_3,event_2,state_1,f311},    {state_3,event_3,state_2,f321},    {state_3,event_5,state_3,f331}  };  stateMachine.transform = stateTran;  EventID inputEvent[15] =   { event_1, event_2, event_3, event_4, event_5,    event_1, event_2, event_3, event_4, event_5,    event_1, event_2, event_3, event_4, event_5 };  int i;  for (i = 0; i < 15; i++) {    runStateMachine(&stateMachine, inputEvent[i]);  }  return 0;}
           

    最後運作結果如下

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)

總結:

    狀态機應用很廣泛,也可以鍛煉我們寫代碼的邏輯思維,看清問題的本質,寫的代碼才能賞心悅目,希望大家能夠多多指點,找到程式設計的樂趣,欣賞到程式設計之美。

猜你喜歡:

C語言、嵌入式位操作精華技巧大彙總

面試題:C語言驗證大小端的幾個方法

【C語言筆記】assert()怎麼用?

C語言代碼優化的一些技巧(四)

c語言狀态機_【程式設計之美】用C語言實作狀态機(實用)