Fundamentals 8 min read

Mastering Finite State Machines in C: From Simple If/Else to Function Pointers

This article explains what a finite state machine (FSM) is, where it is commonly used, and walks through three C implementations—plain if/else, switch‑case, and a function‑pointer table—highlighting their advantages, drawbacks, and providing complete code examples.

ITPUB
ITPUB
ITPUB
Mastering Finite State Machines in C: From Simple If/Else to Function Pointers

Finite State Machine (FSM) is a mathematical model describing a limited set of states and the transitions between them, widely used in parsers, protocol handlers, game AI, and server logic to keep code clear and maintainable.

Typical Application Scenarios

FSMs appear in tokenizers for programming or natural languages, bottom‑up parsers, communication protocol implementations, and game AI, among other domains.

Implementation 1: Using if/else if Statements

The most straightforward method employs a series of if/else if checks on the current state and executes the corresponding action, then manually updates the state variable.

enum {
    GET_UP,
    GO_TO_SCHOOL,
    HAVE_LUNCH,
    GO_HOME,
    DO_HOMEWORK,
    SLEEP,
};

int main() {
    int state = GET_UP;
    while (1) {
        if (state == GET_UP) {
            GetUp();               // perform action
            state = GO_TO_SCHOOL;   // transition
        } else if (state == GO_TO_SCHOOL) {
            Go2School();
            state = HAVE_LUNCH;
        } else if (state == HAVE_LUNCH) {
            HaveLunch();
            state = GO_HOME;
        } else if (state == SLEEP) {
            Go2Bed();
            state = GET_UP;
        }
    }
    return 0;
}

While easy to understand, this approach quickly becomes unwieldy as the number of states grows, leading to code bloat and reduced readability.

Implementation 2: Using switch Statements

A switch on the current state makes the structure clearer, but it still suffers from the same scalability issues when many states are involved.

int state = GET_UP;
while (1) {
    switch (state) {
        case GET_UP:
            GetUp();
            state = GO_TO_SCHOOL;
            break;
        case GO_TO_SCHOOL:
            Go2School();
            state = HAVE_LUNCH;
            break;
        case HAVE_LUNCH:
            HaveLunch();
            state = GO_HOME;
            break;
        case SLEEP:
            Go2Bed();
            state = GET_UP;
            break;
        default:
            break;
    }
}

Implementation 3: Using Function Pointers and a State Table

This method builds a table that maps (event, current state) tuples to an action function and the next state, enabling compact and extensible FSMs suitable for large projects.

/* Define states */
enum {
    GET_UP,
    GO_TO_SCHOOL,
    HAVE_LUNCH,
    DO_HOMEWORK,
    SLEEP,
};

/* Define events */
enum {
    EVENT1 = 1,
    EVENT2,
    EVENT3,
};

/* Table entry structure */
typedef struct {
    int event;               // incoming event
    int CurState;            // current state
    void (*eventActFun)();    // action function
    int NextState;           // state after action
} FsmTable_t;

/* Example action functions */
void GetUp() { /* ... */ }
void Go2School() { /* ... */ }
void HaveLunch() { /* ... */ }
void DoHomework() { /* ... */ }
void Go2Bed() { /* ... */ }

/* State table for "XiaoMing" example */
FsmTable_t XiaoMingTable[] = {
    { EVENT1, SLEEP,        GetUp,        GET_UP },
    { EVENT2, GET_UP,       Go2School,    GO_TO_SCHOOL },
    { EVENT3, GO_TO_SCHOOL, HaveLunch,    HAVE_LUNCH },
    { EVENT1, HAVE_LUNCH,   DoHomework,   DO_HOMEWORK },
    { EVENT2, DO_HOMEWORK,  Go2Bed,       SLEEP },
    // add more rows as needed
};

/* FSM core structure */
typedef struct {
    int curState;
    FsmTable_t *FsmTable;
} FSM_t;

void FSM_Regist(FSM_t *pFsm, FsmTable_t *pTable) {
    pFsm->FsmTable = pTable;
}

void FSM_StateTransfer(FSM_t *pFsm, int state) {
    pFsm->curState = state;
}

void FSM_EventHandle(FSM_t *pFsm, int event) {
    FsmTable_t *pActTable = pFsm->FsmTable;
    void (*eventActFun)() = NULL;
    int NextState = 0;
    int CurState = pFsm->curState;
    int flag = 0;
    for (int i = 0; i < sizeof(XiaoMingTable)/sizeof(FsmTable_t); ++i) {
        if (event == pActTable[i].event && CurState == pActTable[i].CurState) {
            flag = 1;
            eventActFun = pActTable[i].eventActFun;
            NextState = pActTable[i].NextState;
            break;
        }
    }
    if (flag && eventActFun) {
        eventActFun();               // execute action
        FSM_StateTransfer(pFsm, NextState); // move to next state
    }
}

int main() {
    FSM_t fsm = { .curState = SLEEP };
    FSM_Regist(&fsm, XiaoMingTable);
    int event = EVENT1;
    while (1) {
        printf("event %d is coming...
", event);
        FSM_EventHandle(&fsm, event);
        printf("fsm current state %d
", fsm.curState);
        sleep(1); // pause for observation
        // rotate events for demonstration
        event = (event % 3) + 1;
    }
    return 0;
}

The table‑driven approach keeps the core FSM engine tiny; adding new behavior only requires inserting another row into the table, making the design highly maintainable for large‑scale state machines.

Comparison and Takeaways

If/else : simplest but quickly becomes messy with many states.

Switch : clearer than chained if s but still suffers from code duplication for large systems.

Function‑pointer table : most scalable; the FSM logic stays constant while the table grows, enabling easy extensions and better separation of concerns.

All three implementations are demonstrated with a "XiaoMing's day" scenario, showing how the FSM transitions from waking up to going to school, having lunch, doing homework, and sleeping, then repeats.

FSM illustration
FSM illustration
State transition diagram
State transition diagram
FSM runtime output
FSM runtime output
FSM animation
FSM animation
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

c++Finite State Machineif-elseswitchFunction Pointerstate table
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.