C のステート マシン

Waqar Aslam 2023年10月12日
  1. ステート マシンの概要
  2. Function Pointers を使用してステート マシンを作成する
  3. Switch ステートメントを使用してステート マシンを作成する
C のステート マシン

この記事では、C プログラミング言語でのステート マシンの実装について説明します。

ステート マシンの概要

ステート マシンを使用してコードを実装することは、複雑なエンジニアリングの問題を解決するための貴重な設計戦略です。 ステート マシンは、全体的な設計を受け取り、ステート マシンの専門用語で状態と呼ばれる段階に分割します。

すべての州は、特定の機能を実行する責任があります。 一方、イベントは、ステート マシンを状態間で変化させる刺激です。 トランジションとも呼ばれます。

最初に作成されたとき、ほとんどのシステムは簡単でよく整理されていますが、新しい機能が追加されると、イベントの履歴を追跡するために追加のフラグと変数が作成されます。

次に、if ステートメントと else ステートメントを追加して、多くの変数とフラグから作成されたますます複雑な論理式をテストします。

この点で、ステート マシンが役に立ちます。 ステート マシンを適切に使用すると、各分岐点でテストされる条件が簡素化され、プログラム実行のさまざまなモード間の切り替えが容易になります。

ほとんどのリアルタイム システムの動作は、比較的少数の重複しないチャンク (状態) に分割できることがわかります。

各チャンク内のイベント応答は、現在のイベントのみに依存し、過去に発生した一連のイベントには依存しなくなりました。

Function Pointers を使用してステート マシンを作成する

以下は、C でステート マシンを構築するコードの例です。この特定の例では、2つの 状態 スイッチを 10 回前後に生成します。

次に、インデックス番号とそのインデックスに対応する状態を表示します。

#include <stdio.h>

struct state;
typedef void state_fn(struct state *);

struct state {
  state_fn *next;
  int i;
};

state_fn off_state, on_state;

void off_state(struct state *state) {
  printf("%s %i\n", __func__, ++state->i);
  state->next = on_state;
}

void on_state(struct state *state) {
  printf("%s %i\n", __func__, ++state->i);
  state->next = state->i < 10 ? off_state : 0;
}

int main(void) {
  struct state state = {off_state, 0};
  while (state.next) state.next(&state);
}

出力:

off_state 1
on_state 2
off_state 3
on_state 4
off_state 5
on_state 6
off_state 7
on_state 8
off_state 9
on_state 10

Switch ステートメントを使用してステート マシンを作成する

確実なスタートを切るには、プログラムが存在する可能性のある一連の 状態 と、プログラムが処理できる一連の イベント をリストする必要があります。

このプログラムは、START,ITERATE,、または END の 3つの状態のいずれかになります。

enum states {
  START,
  ITERATE,
  END,
} state;

イベント START LOOPINGSHOW_MESSAGE、および STOP LOOPING を処理できます。 START 状態で START LOOPING イベントを受け取った後、プログラムは ITERATE 状態に遷移します。

ITERATE 状態にある間、SHOW_MESSAGE イベントを受信するとメッセージが表示されます。 最後に、STOP LOOPING イベントを受信した後、END 状態に遷移します。

enum events {
  START_LOOPING,
  SHOW_MESSAGE,
  STOP_LOOPING,
};

現在の状態を見て適切なケースを実行する switch ステートメントを使用してみましょう。 STARTITERATE および END と呼ばれる 3つの主な状況があります。

START ケース内で、受信したイベントが START LOOPING の場合、状態が ITERATE に変更されます。 イベントが START LOOPING でない場合、ケースは壊れます。

同様に、ITERATE の場合、受信したイベントが SHOW MESSAGE の場合、メッセージが表示されます。 それ以外の場合は、STOP LOOPING ケースをチェックし、状態を END に変更します。

これらのイベントのいずれも受信されない場合、中断して次のケースに進みます。 最後に、END ケースにより switch ステートメントが終了し、プログラムが終了します。

void switchState(enum events event) {
  switch (state) {
    case START:
      switch (event) {
        case START_LOOPING:
          state = ITERATE;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case ITERATE:
      switch (event) {
        case SHOW_MESSAGE:
          printf("State Machine Ready!\n");
          break;
        case STOP_LOOPING:
          state = END;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case END:
      exit(1);
      break;
  }
}

最後に、switchState() として知られるメソッドを呼び出して、一度に 1つずつ引数の形式でイベントを提供します。

int main(void) {
  switchState(START_LOOPING);
  switchState(SHOW_MESSAGE);
  switchState(STOP_LOOPING);
  return 0;
}

以下は、switch ステートメントを使用した完全なソース コードです。

#include <stdio.h>
#include <stdlib.h>

enum states {
  START,
  ITERATE,
  END,
} state;

enum events {
  START_LOOPING,
  SHOW_MESSAGE,
  STOP_LOOPING,
};

void switchState(enum events event) {
  switch (state) {
    case START:
      switch (event) {
        case START_LOOPING:
          state = ITERATE;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case ITERATE:
      switch (event) {
        case SHOW_MESSAGE:
          printf("State Machine Ready!\n");
          break;
        case STOP_LOOPING:
          state = END;
          break;
        default:
          exit(1);
          break;
      }
      break;
    case END:
      exit(1);
      break;
  }
}

int main(void) {
  switchState(START_LOOPING);
  switchState(SHOW_MESSAGE);
  switchState(STOP_LOOPING);
  return 0;
}

出力:

State Machine Ready!
著者: Waqar Aslam
Waqar Aslam avatar Waqar Aslam avatar

I am Waqar having 5+ years of software engineering experience. I have been in the industry as a javascript web and mobile developer for 3 years working with multiple frameworks such as nodejs, react js, react native, Ionic, and angular js. After which I Switched to flutter mobile development. I have 2 years of experience building android and ios apps with flutter. For the backend, I have experience with rest APIs, Aws, and firebase. I have also written articles related to problem-solving and best practices in C, C++, Javascript, C#, and power shell.

LinkedIn