C言語の関数ポインタで実装する状態遷移

2026-02-06

設計
状態遷移

はじめに

今までC言語で状態遷移を実装するときに、switch文を使った実装をしていました。 しかし、switch文が点在して、追加実装漏れが発生する可能性があります。 そこで今回は、関数ポインタを使った状態遷移を作成したので、共有します。

今回実装する状態遷移

今回は、以下のような状態遷移を実装します。

  • 状態は以下の2つ
    • 状態A
    • 状態B
  • 各状態に始めて遷移したときに、変数を初期化する
  • 各状態でカウンターをインクリメントする

今回実装する状態遷移

switch文による状態遷移の実装

switch文での実装は次のようになります。

#include <stdbool.h>
#include <stdio.h>
// 状態を定義
typedef enum {
STATE_A,
STATE_B,
STATE_NUM,
} EState;
// 状態Aで使用する構造体
typedef struct {
int count;
} StateAContext;
// 状態Bで使用する構造体
typedef struct {
int count;
} StateBContext;
void state_a_on_enter(StateAContext* ctx) {
printf("enter to stateA\n");
ctx->count = 0;
}
EState state_a_do(StateAContext* ctx) {
printf("stateA\n");
ctx->count++;
if (ctx->count >= 3) {
return STATE_B;
}
return STATE_A;
}
void state_b_on_enter(StateBContext* ctx) {
printf("enter to stateB\n");
ctx->count = 0;
}
EState state_b_do(StateBContext* ctx) {
printf("stateB\n");
ctx->count++;
if (ctx->count >= 2) {
return STATE_A;
}
return STATE_B;
}
int main(void) {
StateAContext ctx_a = {0};
StateBContext ctx_b = {0};
EState now = STATE_A;
EState prev = STATE_NUM;
const int count = 10;
for (int i = 0; i < count; i++) {
if (prev != now) {
switch (now) {
case STATE_A:
state_a_on_enter(&ctx_a);
break;
case STATE_B:
state_b_on_enter(&ctx_b);
break;
default:
break;
}
}
prev = now;
switch (now) {
case STATE_A:
now = state_a_do(&ctx_a);
break;
case STATE_B:
now = state_b_do(&ctx_b);
break;
default:
break;
}
}
}

関数ポインタによる状態遷移の実装

#include <stdbool.h>
#include <stdio.h>
typedef enum {
STATE_A,
STATE_B,
STATE_NUM,
} EState;
typedef struct {
int count;
} StateAContext;
typedef struct {
int count;
} StateBContext;
typedef union {
StateAContext a;
StateBContext b;
} Context;
void state_a_on_enter(Context* ctx) {
printf("enter to stateA\n");
ctx->a.count = 0;
}
EState state_a_do(Context* ctx) {
printf("stateA\n");
ctx->a.count++;
if (ctx->a.count >= 3) {
return STATE_B;
} else {
return STATE_A;
}
}
void state_b_on_enter(Context* ctx) {
printf("enter to stateB\n");
ctx->b.count = 0;
}
EState state_b_do(Context* ctx) {
printf("stateB\n");
ctx->b.count++;
if (ctx->b.count >= 2) {
return STATE_A;
} else {
return STATE_B;
}
}
typedef void (*on_enter_fn)(Context* ctx);
typedef EState (*do_fn)(Context* ctx);
typedef struct State {
on_enter_fn on_enter;
do_fn on_do;
} State;
int main(void) {
const State states[STATE_NUM] = {
[STATE_A] =
{
.on_enter = state_a_on_enter,
.on_do = state_a_do,
},
[STATE_B] =
{
.on_enter = state_b_on_enter,
.on_do = state_b_do,
},
};
Context ctx = {0};
EState now = STATE_A;
EState prev = STATE_NUM;
const int N = 10;
for (int i = 0; i < N; i++) {
if (prev != now) {
states[now].on_enter(&ctx);
}
prev = now;
now = states[now].on_do(&ctx);
}
}

まとめ

C言語での状態遷移の実装パターン(switch文を使用した場合、関数ポインタを使用した場合)の紹介をしました。

同じカテゴリの記事