1. 程式人生 > >C語言 有限狀態機

C語言 有限狀態機

1、狀態機的要素

狀態機可歸納為4個要素,即現態、條件、動作、次態。“現態”和“條件”是因,“動作”和“次態”是果。詳解如下:

①現態:是指當前所處的狀態。

②條件:又稱為“事件”。當一個條件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。

③動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。

④次態:條件滿足後要遷往的新狀態。“次態”是相對於“現態”而言的,“次態”一旦被啟用,就轉變成新的“現態”了。

這裡需要注意的兩個問題:

1、避免把某個“程式動作”當作是一種“狀態”來處理。那麼如何區分“動作”和“狀態”?“動作”是不穩定的,即使沒有條件的觸發,“動作”一旦執行完畢就結束了;而“狀態”是相對穩定的,如果沒有外部條件的觸發,一個狀態會一直持續下去。

2、狀態劃分時漏掉一些狀態,導致跳轉邏輯不完整。

所以維護上述一張狀態表就非常必要,而且有意義了。從表中可以直觀看出那些狀態直接存在跳轉路徑,那些狀態直接不存在。如果不存在,就把對應的單元格置灰。每次寫程式碼之前先把表格填寫好,並且對置灰的部分重點review,看看是否有“漏態”,然後才是寫程式碼。QA拿到這張表格之後,寫測試用例也是手到擒來。

有限狀態機FSM

思想廣泛應用於硬體控制電路設計,也是軟體上常用的一種處理方法(軟體上稱為FMM--有限訊息機)。它把複雜的控制邏輯分解成有限個穩定狀態,在每個狀態上判斷事件,變連續處理為離散數字處理,符合計算機的工作特點。同時,因為有限狀態機具有有限個狀態,所以可以在實際的工程上實現。但這並不意味著其只能進行有限次的處理,相反,有限狀態機是閉環系統,有限無窮,可以用有限的狀態,處理無窮的事務。 有限狀態機的工作原理如圖1所示,發生事件(event)後,根據當前狀態(cur_state),決定執行的動作(action),並設定下一個狀態號(nxt_state)。 ------------- | |-------->執行動作action 發生事件event ----->| cur_state | | |-------->設定下一狀態號nxt_state ------------- 當前狀態 圖1 有限狀態機工作原理 e0/a0 --->-- | | -------->---------- e0/a0 | | S0 |----- | -<------------ | e1/a1 | | e2/a2 V ---------- ---------- | S2 |-----<-----| S1 | ---------- e2/a2 ---------- 圖2 一個有限狀態機例項 -------------------------------------------- 當前狀態 s0 s1 s2 | 事件 -------------------------------------------- a0/s0 -- a0/s0 | e0 -------------------------------------------- a1/s1 -- -- | e1 -------------------------------------------- a2/s2 a2/s2 -- | e2 -------------------------------------------- 表1 圖2狀態機例項的二維表格表示(動作/下一狀態) 圖2為一個狀態機例項的狀態轉移圖,它的含義是: 在s0狀態,如果發生e0事件,那麼就執行a0動作,並保持狀態不變; 如果發生e1事件,那麼就執行a1動作,並將狀態轉移到s1態; 如果發生e2事件,那麼就執行a2動作,並將狀態轉移到s2態; 在s1狀態,如果發生e2事件,那麼就執行a2動作,並將狀態轉移到s2態; 在s2狀態,如果發生e0事件,那麼就執行a0動作,並將狀態轉移到s0態; 有限狀態機不僅能夠用狀態轉移圖表示,還可以用二維的表格代表。一般將當前狀態號寫在橫行上,將事件寫在縱列上,如表1所示。其中“--”表示空(不執行動作,也不進行狀態轉移),“an/sn”表示執行動作an,同時將下一狀態設定為sn。表1和圖2表示 的含義是完全相同的。 觀察表1可知,狀態機可以用兩種方法實現:豎著寫(在狀態中判斷事件)和橫著寫(在事件中判斷狀態)。這兩種實現在本質上是完全等效的,但在實際操作中,效果卻截然 不同。

豎著寫C程式碼片段

cur_state = nxt_state; switch(cur_state){ //在當前狀態中判斷事件 case s0: //在s0狀態 if(e0_event){ //如果發生e0事件,那麼就執行a0動作, 並保持狀態不變; 執行a0動作; //nxt_state = s0; //因為狀態號是自身,所以可以刪除此句 ,以提高執行速度。 } else if(e1_event){ //如果發生e1事件,那麼就執行a1動作, 並將狀態轉移到s1態; 執行a1動作; nxt_state = s1; } else if(e2_event){ //如果發生e2事件,那麼就執行a2動作, 並將狀態轉移到s2態; 執行a2動作; nxt_state = s2; } break; case s1: //在s1狀態 if(e2_event){ //如果發生e2事件,那麼就執行a2動作, 並將狀態轉移到s2態; 執行a2動作; nxt_state = s2; } break; case s2: //在s2狀態 if(e0_event){ //如果發生e0事件,那麼就執行a0動作, 並將狀態轉移到s0態; 執行a0動作; nxt_state = s0; } } 橫著寫(在事件中判斷狀態)C程式碼片段
//e0事件發生時,執行的函式 void e0_event_function(int * nxt_state) { int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //觀察表1,在e0事件發生時,s1處為空 case s2: 執行a0動作; *nxt_state = s0; } } //e1事件發生時,執行的函式 void e1_event_function(int * nxt_state) { int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //觀察表1,在e1事件發生時,s1和s2處為 空 執行a1動作; *nxt_state = s1; } } //e2事件發生時,執行的函式 void e2_event_function(int * nxt_state) { int cur_state; cur_state = *nxt_state; switch(cur_state){ case s0: //觀察表1,在e2事件發生時,s2處為空 case s1: 執行a2動作; *nxt_state = s2; } }