您还未登录!|登录|注册|帮助CSDN首页资讯论坛博客下载搜索更多CTO俱乐部学生大本营培训充电移动开发软件研发 云计算程序员 TUPguocai_yao 的专栏条新通知登录注册欢迎退出我的博客配置J-j—?写文章文章管理博客首页全站 当前博客 空间博客好友相册留言用户操作[留言] [发消息] [加为好友] 姚国才 ID:guocai_yao共 19660 次访问,排名 9473,好友 29 人,关注者 35 人态度决定一切姚国才的文章 原创 47 篇 翻译 0 篇 转载 13 篇 评论 25 篇订阅我的博客[编辑]guocai_yao的公告[编辑]文章分类APUE(Advanced Programming In The Unix EnvironmentCC++Programming Tips skillsThe C Programming LanguageUnix 环境高级编程)读书笔记 VC 及其 IDE 单片机 数据结构 琐碎 他山之玉 小想法 硬件电路的那些事儿[编辑]EmbeddedSystemAquarius (其中还有英文网站)[编辑]高手&大师 ammana_babi Richard StallmanRoland McGrathsteedhorsetaodm侯捷周立功徐艺波个人网站艺术编程陈莉君[编辑]好书推荐C++学习推荐书目c 语言的提高[编辑]好友袁东存档2010年 05月(8)2010年 04月(2)2010年 01月(5)2009年 08月(1)2009年 06月(1)2009年 05月(4)2009年 04月(5)2009年 03月(8)2009年 02月(1)2009年 01月(1)2008年 05月(21)2008年 04月(3)公告:CSDN 产品事业部开设官方博客了!来关注我们的一举一动吧![意见反馈][官方博客] C 语言实现有限状态机 收藏以下是转载内容:☆ 传说中的分隔符一 ☆来源 1:转载 1】有限状态机的实现 < type="text/javascript"> 有限状态机(Finite State Machine或者Finite State Automata是软件领域中一种重要的工具, 很多东西的模型实际上就是有限状态机。
最近看了一些游戏编程AI的材料,感觉游戏中的AI,第一要说的就是有限状态机来实现精 灵的AI,然后才是A*寻路,其他学术界讨论比较多的神经网络、模糊控制等问题还不是很 热FSM的实现方式:1) switch/case 或者 if/else这无意是最直观的方式,使用一堆条件判断,会编程的人都可以做到,对简单小巧的状态机 来说最合适,但是毫无疑问,这样的方式比较原始,对庞大的状态机难以维护2) 状态表 维护一个二维状态表,横坐标表示当前状态,纵坐标表示输入,表中一个元素存储下一个状 态和对应的操作这一招易于维护,但是运行时间和存储空间的代价较大3) 使用 State Pattern使用State Pattern使得代码的维护比switch/case方式稍好,性能上也不会有很多的影响,但 是也不是100%完美不过Robert C. Martin做了两个自动产生FSM代码的工具,for java 和 for C++ 各一个,在 的输入是纯文本的状态机描述,自动产生符合State Pattern的代码,这样developer的工作只 需要维护状态机的文本描述,每必要冒引入bug的风险去维护code。
4) 使用宏定义描述状态机一般来说,C++编程中应该避免使用#define,但是这主要是因为如果用宏来定义函数的话, 很容易产生这样那样的问题,但是巧妙的使用,还是能够产生奇妙的效果MFC就是使用宏 定义来实现大的架构的在实现FSM的时候,可以把一些繁琐无比的if/else还有花括号的组合放在宏中,这样,在 代码中可以 3)中状态机描述文本一样写,通过编译器的预编译处理产生1)一样的效果, 我见过产生C代码的宏,如果要产生C++代码,己软MFC可以,那么理论上也是可行的评】状态表的实现方法,《C专家编程》第8章有具体说明,转载【6】☆ 传说中的分隔符 ☆来源 2: 【转载2】有限状态机的c实现2007-05-11 15:12網絡上可以搜索到很多有限狀態機的代碼和理論分析,這兒僅僅是做一個簡單的例子,僅供 入門參考这儿以四位密码校验作为状态机的例子,连续输入2479 就可以通过密码测试一个非常简 单的例子,在实际的状态机实例中,状态转移表要更復雜一些,不過方式非常類似在狀態 查詢的地方可以做優化,同時對于輸入量也可以做有效性優化具體代碼如下:view plaincopy to clipboardprint? c.htypedef enum{STATE1 = 1,STATE2,STATE3,STATE4,STATE5,//password pass//...ADD here }STATE;typedef enum{ INPUT1 = '2', INPUT2 = '4', INPUT3 = '7', INPUT4 = '9', }INPUT;typedef struct{STATE cur_state;INPUT input;STATE next_state; }STATE_TRANS;c.htypedef enum{STATE1 = 1,STATE2,STATE3,STATE4,STATE5,//password pass//...ADD here }STATE;typedef enum{ INPUT1 = '2', INPUT2 = '4', INPUT3 = '7', INPUT4 = '9', }INPUT;typedef struct{STATE cur_state; INPUT input;STATE next_state; }STATE_TRANS;c.c#include #include "c.h"STATE_TRANS state_trans_arry[] ={{STATE1,INPUT1,STATE2},{STATE2,INPUT2,STATE3},{STATE3,INPUT3,STATE4},{STATE4,INPUT4,STATE5},};#define STATE_TRANS_CNT (sizeof(state_trans_arry)/sizeof(state_trans_arry[0]))int main(){int i;char ch;STATE state_machine = STATE1;while(ch != 'e'){ch = getchar();if((ch >= '0') && (ch <= '9'))//for digit password input only{for(i = 0;i < STATE_TRANS_CNT;i++){if((ch == state_trans_arry[i].input) && (state_machine == state_trans_arry[i].cur_state)) {state_machine = state_trans_arry[i].next_state;continue;}else if(i == (STATE_TRANS_CNT - 1))//no transfer match,reset state{state_machine = STATE1;}}if(state_machine == STATE5)printf("Password correct,state transfer machine pass!\n");}}return 0;}c.c#include #include "c.h"STATE_TRANS state_trans_arry[] ={{STATE1,INPUT1,STATE2},{STATE2,INPUT2,STATE3},{STATE3,INPUT3,STATE4},{STATE4,INPUT4,STATE5},};#define STATE_TRANS_CNT (sizeof(state_trans_arry)/sizeof(state_trans_arry[0]))int main(){int i;char ch;STATE state_machine = STATE1;while(ch != 'e'){ch = getchar();if((ch >= '0') && (ch <= '9'))//for digit password input only{for(i = 0;i < STATE_TRANS_CNT;i++){if((ch == state_trans_arry[i].input) && (state_machine == state_trans_arry[i].cur_state)){state_machine = state_trans_arry[i].next_state;continue;}else if(i == (STATE_TRANS_CNT - 1))//no transfer match,reset state{state_machine = STATE1;}}if(state_machine == STATE5)printf("Password correct,state transfer machine pass!\n");}}return 0;}【评】在VC6下运行该程序并没有达到目的,即连续输入字符2479也没有任何输出信息,个人根据转载第一遍文章的 FSM 的实现的第一种方法,见【原创之源程序】☆ 传说中的分隔符 ☆来源 3: 【转载 3】有限状态机自动机状态图--一个图的数据结构!1.while + switch;2. 状态机:就是指定系统的所有可能的状态及状态间跳转的条件,然后设一个初始状态输入 给这台机器,机器就会自动运转,或最后处于终止状态,或在某一个状态不断循环。
游戏中状态切换是很频繁的可能以前要切换状态就是if〜else,或者设标志,但这些都不 太结构化, 如果把它严格的设为一种标准的状态机,会清楚的多比如控制一扇门的运动, 初始时门是关的, 当有力作用在门上时, 门开始慢慢打开,力 的作用完后,门渐渐停止不动, 当有反向的力时,门又渐渐关上, 知道回到初始关的状态 这个你会怎么来编程实现呢 似乎很麻烦, 的确,没有状态机的思想时会很烦,设很多标 志,一堆if条件用状态机的话,不只是代码更清晰, 关键是更符合逻辑和自然规律, 不同状态不同处理,满足条件则跳转到相关状态伪码如下:enum{CLOSED, // 关上状态CLOSING, // 正在关状态OPENED, // 打开状态OPENING, // 正在开的状态 }doorState = CLOSED; // 初始为关Update() {switch(doorState) case CLOSED:if (有人推门) doorState = OPENING; // 跳转到正在开状态 break;case OPENING:door.Rotation += DeltaAngle; // 门的旋转量递增if (门的速度为零) / / 力的作用已去 doorState = OPENED; // 跳转到开状态 break;case OPENED: if (有人关门) doorState = CLOSING; break;case CLOSING:door.Rotation -= DeltaAngle; // 门的旋转量递减if (门的旋转角度减为零) doorState = CLOSED; // 门已关上 break;}// 而绘制代码几乎不用怎么变, 门就是会严格按照状态机的指示去运动, 该停就会停 Render(){RotateView(door.Rotation); DrawDoor(door.Position);} enum {CLOSED, // 关上状态CLOSING, // 正在关状态OPENED, // 打开状态OPENING, // 正在开的状态 }doorState = CLOSED; // 初始为关Update(){switch(doorState)case CLOSED:if (有人推门)doorState = OPENING; // 跳转到正在开状态 break;case OPENING:door.Rotation += DeltaAngle; // 门的旋转量递增if (门的速度为零) / / 力的作用已去 doorState = OPENED; // 跳转到开状态 break;case OPENED:if (有人关门)doorState = CLOSING;break;case CLOSING:door.Rotation -= DeltaAngle; // 门的旋转量递减if (门的旋转角度减为零)doorState = CLOSED; // 门已关上 break;}// 而绘制代码几乎不用怎么变, 门就是会严格按照状态机的指示去运动, 该停就会停 Render(){RotateView(door.Rotation); DrawDoor(door.Position);}这是一个简单但很典型的例子, 状态机的应用太多了。
就说一个基本游戏的运转: (用到了一个状态然后还有子状态) UpdateGame()BEGIN;switch(gameState)case 等待选择菜单: //它有三个子状态if (选择菜单项==开始){游戏初始;gameState = 开始游戏}if (选择菜单项==选项)gameState = 设置if (选择菜单项==退出)gameState = 退出case 开始:游戏运行;if (用户按退出键) gameState = 等待选择菜单 ;...其他的状态跳转处理; case 退出 : 释放资源;退出;case 设置:分别处理不同的选项, 跳转不同的子状态 case // 其他状态的处理END;UpdateGame()BEGIN;switch(gameState)case 等待选择菜单: //它有三个子状态 if (选择菜单项==开始) {游戏初始;gameState = 开始游戏}if (选择菜单项==选项) gameState = 设置if (选择菜单项==退出)gameState = 退出case 开始:游戏运行;if (用户按退出键)gameState = 等待选择菜单 ;...其他的状态跳转处理;case 退出:释放资源;退出;case 设置:分别处理不同的选项, 跳转不同的子状态;case // 其他状态的处理END;某一个状态可以包含更多的子状态, 这样最好是同一层次的状态设为一个枚举, 并分到另 一个 switch 处理如 enum STATES{statel, state2, state3}; state2又包含若干状态则再定义 enum SUB_STATE2{sub_state2_1, sub_state2_2, sub_state2_3,};想很多基本的渲染效果,如淡入淡出, 闪烁等等, 用状态的思想会事半功倍,思路也更 清晰。
其实像Opengl, Direct3D这样的渲染引擎本身就是状态机,当你设置渲染的状态,这台机 器就保持这个状态进行渲染工作,如保持光照位置,保持片元颜色, 直到你再次改变它的 状态状态机的应用实在太广, 相关理论也很多, 最近上课学的随机过程里也讲到一些, 数字 电路里的时序逻辑器件也是用状态机来描述 这些不必多说了总之, 用状态机的角度去看待问题, 往往能把比较复杂的系统分解为能单独处理的众多子 状态, 处理也会得心应手希望大家多用它, 很好的东西推荐这个:[程序员杂志2004.8月刊_state模式和composite模式实现的状态机引擎]个人感觉状态机的几个不同实现阶段:1、 switch/case最原始的实现方式,是很多的c程序员习惯采用的方式2、 查找表[状态、事件、动作],稍微做了一点改进有点类似MFC的雏形3、 在以上基础上做的一些改进或者变体[比如用一个栈结构,激活的状态位于栈顶,自动的映射事件和动作的对应,再或者通过一 些巧妙的宏等手段进行包装但是线性结构在实际中使用比较受限、过于技巧性的宏比较难 于理解...]4、面向对象的设计下、灵活运用模式如上面给出的链接重用性和灵活性方面都有不错 的表现。
沿袭类似的设计思路、根据实际开发内容进行改造后利用评】:伪代码部分,可以帮助很好的理解【转载1】文章中叙述的 FSM 的实现方法1;查 找表[状态、事件、动作],稍微做了一点改进有点类似 MFC 的雏形类似于【转载 1】文章 中叙述的FSM的实现方法2 (状态表) ☆来源 4:【转载 4】fsm implemented in C code(FSM 状态机用 C 实现)用 C 语言实现一个状态机,很简单,和大家分享这是我做毕业设计时,用nRF24L01组建了一个简单的网络,做的一个小的状态机,网络中 三个节点,开始拓扑为网状,后来为星型include #include #include //Finite state machine declaration//state declaration#define IDLE 0 //idle state in rx mode#define M_BROADCAST 1 //broadcast state in tx mode,broadcast to be a master point#define M_WAIT_BROADCAST_ACK 2 //wait for broadcast ack state in rx mode,wait for the point ack in a specific time window#define M_WAIT_COMMAND 3 //wait for command state,wait for PC command via UART#define M_BROADCAST_CANCEL 4 //broadcast cancel state,broadcast to cancel master point#define S_BROADCAST_ACK 5 //slave mode,send back self physical address#define S_WAIT_COMMAND 6 //slave mode, wait for command from the master point//state transition trig//used in master modeint isReqBeMaster = 0;//Is PC request the point to be master?int isTimeout = 0;//Is time out?int isReqCancelMaster = 0;//Is request to cancel master?//used in slave modeint isRxBroadcast = 0;//Is there a point broadcast to be master? int isRxBroadcastCancel = 0;//Is receive broadcast cancel master?typedef struct fsmtag{int state; //stateint timeouttime; //time out time in milliseconds}fsm;//function prototypeint main(){fsm f;f.state = IDLE;f.timeouttime = 0;while(1){switch(f.state){case IDLE:puts("IDLE\nWait for isReqBeMaster(1/0) isRxBroadcast(1/0):"); scanf("%d %d",&isReqBeMaster,&isRxBroadcast); if(isReqBeMaster){f.state = M_BROADCAST;break;}else if(isRxBroadcast){f.state = S_BROADCAST_ACK;break;}elsebreak;case M_BROADCAST:puts("M_BROADCAST\nBroadcasting...\n");f.state = M_WAIT_BROADCAST_ACK;case M_WAIT_BROADCAST_ACK:puts("M_WAIT_BROADCAST_ACK\nWaiting for isTimeout(1/0):"); scanf("%d",&isTimeout);if(isTimeout)f.state = M_WAIT_COMMAND; break;} else break;case M_WAIT_COMMAND: puts("M_WAIT_COMMAND\nWaiting for isReqCancelMaster(1/0):"); scanf("%d",&isReqCancelMaster);if(isReqCancelMaster) {f.state = IDLE; break;} else break;//Slave mode routinecase S_BROADCAST_ACK: puts("S_BROADCAST_ACK\nAcking...\n"); f.state = S_WAIT_COMMAND;break;case S_WAIT_COMMAND: puts("S_WAIT_COMMAND\nWaiting for isRxBroadcastCancel(1/0):"); scanf("%d",&isRxBroadcastCancel);if(isRxBroadcastCancel) {f.state = IDLE;break;} else break;default: puts("default"); printf("%d\n",rand()); f.state = IDLE;}}return 0;}#include #include #include //Finite state machine declaration//state declaration#define IDLE 0 //idle state in rx mode#define M_BROADCAST 1 //broadcast state in tx mode,broadcast to be a master point#define M_WAIT_BROADCAST_ACK 2 //wait for broadcast ack state in rx mode,wait for the point ack in a specific time window#define M_WAIT_COMMAND 3 //wait for command state,wait for PC command via UART#define M_BROADCAST_CANCEL 4 //broadcast cancel state,broadcast to cancel master point#define S_BROADCAST_ACK 5 //slave mode,send back self physical address#define S_WAIT_COMMAND 6 //slave mode, wait for command from the master point//state transition trig//used in master modeint isReqBeMaster = 0;//Is PC request the point to be master?int isTimeout = 0;//Is time out?int isReqCancelMaster = 0;//Is request to cancel master?//used in slave modeint isRxBroadcast = 0;//Is there a point broadcast to be master?int isRxBroadcastCancel = 0;//Is receive broadcast cancel master?typedef struct fsmtag{int state; //stateint timeouttime; //time out time in milliseconds}fsm;//function prototypeint main(){fsm f;f.state = IDLE; f.timeouttime = 0;while(1){switch(f.state){case IDLE:puts("IDLE\nWait for isReqBeMaster(1/0) isRxBroadcast(1/0):"); scanf("%d %d",&isReqBeMaster,&isRxBroadcast);if(isReqBeMaster){f.state = M_BROADCAST; break;}else if(isRxBroadcast){f.state = S_BROADCAST_ACK; break;}else break;case M_BROADCAST: puts("M_BROADCAST\nBroadcasting...\n"); f.state = M_WAIT_BROADCAST_ACK;case M_WAIT_BROADCAST_ACK: puts("M_WAIT_BROADCAST_ACK\nWaiting for isTimeout(1/0):"); scanf("%d",&isTimeout);if(isTimeout){f.state = M_WAIT_COMMAND; break;}else break;case M_WAIT_COMMAND: puts("M_WAIT_COMMAND\nWaiting for isReqCancelMaster(1/0):"); scanf("%d",&isReqCancelMaster);if(isReqCancelMaster){f.state = IDLE; break;}else break;//Slave mode routinecase S_BROADCAST_ACK: puts("S_BROADCAST_ACK\nAcking...\n"); f.state = S_WAIT_COMMAND; break;case S_WAIT_COMMAND: puts("S_WAIT_COMMAND\nWaiting for isRxBroadcastCancel(1/0):"); scanf("%d",&isRxBroadcastCancel);if(isRxBroadcastCancel)f.state = IDLE; break;}elsebreak;default: puts("default"); printf("%d\n",rand()); f.state = IDLE;}}return 0;}评】:很实用的一个状态机程序☆ 传说中的分隔符 ☆来源 5:http://redbug.21ic.org/user1/349/archives/2007/44609.html【转载 5】状态机的两种写法2004/12/26 asdjf@yy20041226-1v1有限状态机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 一个有限状态机实例当前状态 s0s1s2| 事件a0/s0--a0/s0| e0a1/s1----| e1a2/s2a2/s2| e2表1 图2 状态机实例的二维表格表示(动作/下一状态)图2 为一个状态机实例的状态转移图,它的含义是:在 s0 状态,如果发生 e0 事件,那么就执行 a0 动作,并保持状态不变; 如果发生el事件,那么就执行al动作,并将状态转移到si态; 如果发生e2事件,那么就执行a2动作,并将状态转移到s2态; 在si状态,如果发生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){态转移到si态;执行 a1 动作;nxt_state = si;}else if(e2_event){态转移到 s2 态;执行 a2 动作;nxt_state = s2;} break;case si:if(e2_event){状态转移到 s2 态;执行 a2 动作;nxt_state = s2;} break;case s2:if(e0_event){状态转移到 s0 态;执行 a0 动作;〃如果发生el事件,那么就执行al动作,并将状〃如果发生e2事件,那么就执行a2动作,并将状//在 sl 状态〃如果发生e2事件,那么就执行a2动作,并将//在 s2 状态//如果发生 e0 事件,那么就执行 a0 动作,并将nxt_state = s0;横着写(在事件中判断状态)C代码片段//e0 事件发生时,执行的函数void e0_event_function(int * nxt_state) {int cur_state;cur_state = *nxt_state;switch(cur_state){case sO: 〃观察表1,在e0事件发生时,si处为空case s2:执行aO动作;*nxt_state = sO;} }//e1 事件发生时,执行的函数void e1_event_function(int * nxt_state) {int cur_state;cur_state = *nxt_state;switch(cur_state){case sO: //观察表 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 sO: 〃观察表1,在e2事件发生时,s2处为空case s1:执行 a2 动作;*nxt_state = s2;} }上面横竖两种写法的代码片段,实现的功能完全相同,但是,横着写的效果明显好于竖 着写的效果。
理由如下:1、 竖着写隐含了优先级排序(其实各个事件是同优先级的),排在前面的事件判断将毫 无疑问地优先于排在后面的事件判断这种 if/else if 写法上的限制将破坏事件间原有的关 系而横着写不存在此问题2、 由于处在每个状态时的事件数目不一致,而且事件发生的时间是随机的,无法预先 确定,导致竖着写沦落为顺序查询方式,结构上的缺陷使得大量时间被浪费对于横着写, 在某个时间点,状态是唯一确定的,在事件里查找状态只要使用 switch 语句,就能一步定 位到相应的状态,延迟时间可以预先准确估算而且在事件发生时,调用事件函数,在函数 里查找唯一确定的状态,并根据其执行动作和状态转移的思路清晰简洁,效率高,富有美感总之,我个人认为,在软件里写状态机,使用横着写的方法比较妥帖竖着写的方法也不是完全不能使用,在一些小项目里,逻辑不太复杂,功能精简,同时 为了节约内存耗费,竖着写的方法也不失为一种合适的选择在FPGA类硬件设计中,以状态为中心实现控制电路状态机(竖着写)似乎是唯一的选择, 因为硬件不太可能靠事件驱动(横着写)不过,在FPGA里有一个全局时钟,在每次上升沿 时进行状态切换,使得竖着写的效率并不低。
虽然在硬件里竖着写也要使用IF/ELSIF这类 查询语句(用VHDL开发),但他们映射到硬件上是组合逻辑,查询只会引起门级延迟(ns量 级),而且硬件是真正并行工作的,这样竖着写在硬件里就没有负面影响因此,在硬件设 计里,使用竖着写的方式成为必然的选择这也是为什么很多搞硬件的工程师在设计软件状 态机时下意识地只使用竖着写方式的原因,盖思维定势使然也TCP 和 PPP 框架协议里都使用了有限状态机,这类软件状态机最好使用横着写的方式 实现以某 TCP 协议为例,见图 3,有三种类型的事件:上层下达的命令事件;下层到达 的标志和数据的收包事件;超时定时器超时事件上层命令(open,close)事件| TCP | < 超时事件 timeoutRST/SYN/FIN/ACK/DATA 等收包事件图 3 三大类 TCP 状态机事件由图3可知,此TCP协议栈采用横着写方式实现,有3种事件处理函数,上层命令处 理函数(如tcp_close);超时事件处理函数(tmr_slow);下层收包事件处理函数(tcp_process) 值得一提的是,在收包事件函数里,在各个状态里判断RST/SYN/FIN/ACK/DATA等标志(这 些标志类似于事件),看起来象竖着写方式,其实,如果把包头和数据看成一个整体,那么 RST/SYN/FIN/ACK/DATA等标志就不必被看成独立的事件,而是属于同一个收包事件里的 细节,这样,就不会认为在状态里查找事件,而是总体上看,是在收包事件里查找状态(横 着写)。
在PPP里更是到处都能见到横着写的现象,有时间的话再细说我个人感觉在实现PPP 框架协议前必须了解横竖两种写法,而且只有使用横着写的方式才能比较完美地实现PPP评】:看不大懂,先留着,有备无患传说中的分隔符 来源 6:【转载6】用C语言实现有限状态机--读《C专家编程》有限状态机(finite state machine)是一个数学概念,如果把它运用于程序中,可以发挥很大的 作用它是一种协议,用于有限数量的子程序("状态")的发展变化每个子程序进行一些处 理并选择下一种状态(通常取决于下一段输入)有限状态机(FSM)可以用作程序的控制结构°FSM对于那些基于输入的在几个不同的可选动 作中进行循环的程序尤其合适投币售货机就是FSM的一个好例子另外一个你可以想到 的复杂的例子就是你正在用的东西,想到了吗?没错,就是操作系统在投币售货机的例子 中,输入是硬币,输出是待售商品,售货机有"接受硬币", "选择商品", "发送商品"和"找零 钱"等几种状态它的基本思路是用一张表保存所有可能的状态,并列出进入每个状态时可能执行的所有动 作,其中最后一个动作就是计算(通常在当前状态和下一次输入字符的基础上,另外再经过 一次表查询)下一个应该进入的状态。
你从一个"初始状态"开始在这一过程中,翻译表可 能告诉你进入了一个错误状态,直到到达结束状态在C语言中,有好几种方法可以用来表达FSM,但它们绝大多数都是基于函数指针数组 一个函数指针数组可以像下面这样声明:void (*state[MAX_STATES]) ();如果知道了函数名,就可以像下面这样对数组进行初始化extern int a(),b(),c(),d();int (*state[]) ()={a,b,c,c};可以通过数组中的指针来调用函数:(*state[i]) ();所有函数必须接受同样的参数,并返回同种类型的返回值 (除非你把数组元素做成一个联 合)函数指针是很有趣的注意,我们可以去掉指针形式,把上面的调用写成:state[i] ();甚至(******state[i]) ();这是一个在 ANSI C 中流行的不良方法:调用函数和通过指针调用函数(或任意层次的指针 间接引用)可以使用同一种语法如果你想干得漂亮一点,可以让状态函数返回一个指向通用后续函数的指针,并把它转换为 适当的类型这样,就不需要全局变量了如果你不想搞得太花哨,可以使用一个 switch 语句作为一种简朴的状态机,方法是赋值给控制变量并把 switch 语句放在循环内部。
关于 FSM 还有最后一点需要说明:如果你的状态函数看上去需要多个不同的参数,可以考虑使 用一个参数计数器和一个字符串指针数组,就像 main 函数的参数一样我们熟悉的 int argc,char *argv[]机制是非常普遍的,可以成功地应用在你所定义的函数中☆ 传说中的分隔符 ☆【原创之源程序】密码锁(最简单的实现switch/if-else形的)#include #include #include typedef enum{STATE0 = 0,STATE1,STATE2,STATE3,STATE4,//password pass//...ADD here}STATE;typedef enum{INPUT1 = '2',INPUT2 = '4',INPUT3 = '7',INPUT4 = '9', }INPUT;int main(){char ch;STATE current_state = STATE0;while(1){printf("请输入数字进行解码:”);while((ch = getchar()) != '\n'){if((ch < '0') || (ch > '9')){printf(”非数字,请重新输入!\n");break;}switch(current_state){case STATE0:if(ch == '2') current_state = STATE1;break;case STATE1:if(ch == '4') current_state = STATE2;break;case STATE2:if(ch == '7') current_state = STATE3;break;case STATE3:if(ch == '9') current_state = STATE4;break;default:current_state = STATE0;break;}}if(current_state == STATE4){printf("Correct, lock is open!\n");break;}elseprintf("Wrong, unlocked!\n");}return 0;}#include #include #include typedef enum{STATE0 = 0,STATE1,STATE2,STATE3,STATE4,//password pass //...ADD here}STATE;typedef enum{INPUT1 = '2',INPUT2 = '4',INPUT3 = '7',INPUT4 = '9',}INPUT;int main()char ch;STATE current_state = STATE0;while(1){printf("请输入数字进行解码:”);while((ch = getchar()) != '\n'){if((ch < '0') || (ch > '9')){printf("非数字,请重新输入!\n");break;}switch(current_state){case STATE0:if(ch == '2') current_state = STATE1;break;case STATE1:if(ch == '4') current_state = STATE2;break;case STATE2:if(ch == '7') current_state = STATE3;break;case STATE3:if(ch == '9') current_state = STATE4;break;default:current_state = STATE0;break;}}if(current_state == STATE4){printf("Correct, lock is open!\n");break;}elseprintf("Wrong, unlocked!\n");}return 0;}【另】还有两个演示程序面向对象的:面向过程的: 发表于 @ 2009年04月17日 14:36:00 | 评论( 1 ) | 编辑| 举报| 收藏旧一篇:【转】中缀表达式转换成前缀表达式和后缀表达式丨新一篇:KMP算法。