Mastering C State Machines: Switch‑Case, Table‑Driven, and Function‑Pointer Techniques
This article explains how to implement finite state machines in C using three approaches—switch‑case, table‑driven, and function‑pointer—detailing their structures, code examples, trade‑offs, and extensions such as compressed tables and hierarchical state machines for robust embedded systems.
Overview
Implementing a state machine in C revolves around three essential elements: state , event , and response . The article breaks down three common implementation methods and discusses their advantages, pitfalls, and extensions.
1. Switch‑Case Method
The linear switch‑case approach nests a switch for events inside a switch for states. Example code (List 4) shows how each state case contains an inner switch handling relevant events, calling action functions, and updating the state variable.
switch(StateVal){
case S0:
switch(EvntID){
case E1: action_S0_E1(); StateVal = new_state; break;
case E2: action_S0_E2(); StateVal = new_state; break;
// ...
}
break;
case S1:
// ...
break;
default:
break;
}Two nesting styles are possible: state‑nested‑event and event‑nested‑state . The order of cases matters for performance; frequently used states/events should be placed early.
2. Table‑Driven Method
This method maps states (rows) and events (columns) onto a two‑dimensional array. Each cell contains a fsm_node structure (List 5) with a function pointer fpAction and a next‑state value u8NxtStat.
struct fsm_node{ void (*fpAction)(void* pEvnt); INT8U u8NxtStat; };The table is indexed by the current state and event type, allowing constant‑time lookup. Enumerations are recommended for both state and event sets to ensure a contiguous integer range. The table‑driven approach yields higher average efficiency than nested switch‑case but requires careful maintenance of the matrix.
3. Compressed Table‑Driven Method
Also called the “truncated table‑driven method,” it collapses the two‑dimensional matrix into a one‑dimensional array where each element is a fsm_node that returns the next state instead of storing it.
struct fsm_node{ INT8U (*fpAction)(void* pEvnt); INT8U u8StatChk; };The fpAction function now returns the new state, enabling integration with an Extended State Machine where the transition can depend on additional conditions. The u8StatChk field validates that the current state matches the node’s expected state, preventing illegal accesses.
4. Function‑Pointer Method
Here the state itself is represented by a function pointer. Each action function may return the address of the next action function, forming a chain of calls. This method offers maximum flexibility but lacks built‑in safety checks, making it prone to “run‑away” pointers if corrupted.
Extended and Hierarchical State Machines
The article briefly mentions Extended State Machines (ESM) , which evaluate guard conditions before deciding actions and transitions, and Hierarchical State Machines (HSM) , where a parent state machine contains child machines, providing a more powerful modeling tool for complex problems.
In practice, mastering finite state machines involves three steps: (1) draw a complete state‑transition diagram, (2) extract states, events, and actions, and (3) implement the chosen method while optimizing for readability, safety, and performance.
Images illustrate the table‑driven layout and the compressed table concept.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Linux
Focused on sharing Linux/Unix content, covering fundamentals, system development, network programming, automation/operations, cloud computing, and related professional knowledge.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
