狀態(tài)機基本術(shù)語現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。 條件:又稱為“事件”,當(dāng)一個條件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。 動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。 次態(tài):條件滿足后要遷往的新狀態(tài)?!按螒B(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。 
傳統(tǒng)有限狀態(tài)機Fsm實現(xiàn)方法
如圖,是一個定時計數(shù)器,計數(shù)器存在兩種狀態(tài),一種為設(shè)置狀態(tài),一種為計時狀態(tài) 設(shè)置狀態(tài) 計時狀態(tài) 嵌套switch /***************************************
1.列出所有的狀態(tài)
***************************************/ typedef enum{ SETTING, TIMING }STATE_TYPE; /***************************************
2.列出所有的事件
***************************************/ typedef enum{UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT }EVENT_TYPE; /***************************************
3.定義和狀態(tài)機相關(guān)結(jié)構(gòu)
***************************************/ struct bomb { uint8_t state; uint8_t timeout; uint8_t code; uint8_t defuse_code; }bomb1; /***************************************
4.初始化狀態(tài)機
***************************************/ void bomb1_init(void) { bomb1.state = SETTING; bomb1.defuse_code = 6; //0110 } /***************************************
5. 狀態(tài)機事件派發(fā)
***************************************/ void bomb1_fsm_dispatch(EVENT_TYPE evt ,void* param) { switch(bomb1.state) { case SETTING: { switch(evt) { case UP_EVT: // '+' 按鍵按下事件if(bomb1.timeout< 60) ++bomb1.timeout; bsp_display(bomb1.timeout); break; case DOWN_EVT: // '-' 按鍵按下事件 if(bomb1.timeout > 0) --bomb1.timeout; bsp_display(bomb1.timeout); break; case ARM_EVT: // '確認' 按鍵按下事件 bomb1.state = TIMING; bomb1.code = 0; break; } } break;
case TIMING: { switch(evt) { case UP_EVT: // '+' 按鍵按下事件 bomb1.code = (bomb1.code <<1) |0x01; break; case DOWN_EVT: // '-' 按鍵按下事件 bomb1.code = (bomb1.code <<1);
break; case ARM_EVT: // '確認' 按鍵按下事件 if(bomb1.code == bomb1.defuse_code){ bomb1.state = SETTING; } else{ bsp_display('bomb!') } break; case TICK_EVT: if(bomb1.timeout) { --bomb1.timeout; bsp_display(bomb1.timeout); } if(bomb1.timeout == 0) { bsp_display('bomb!') } break; }
}break; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

狀態(tài)表二維狀態(tài)轉(zhuǎn)換表 狀態(tài)機可以分為狀態(tài)和事件 ,狀態(tài)的躍遷都是受事件驅(qū)動的,因此可以通過一個二維表格來表示狀態(tài)的躍遷。  (*) 僅當(dāng)( code == defuse_code) 時才發(fā)生到setting 的轉(zhuǎn)換。 /*1.列出所有的狀態(tài)*/ enum { SETTING, TIMING, MAX_STATE }; /*2.列出所有的事件*/ enum { UP_EVT, DOWN_EVT, ARM_EVT, TICK_EVT, MAX_EVT };
/*3.定義狀態(tài)表*/ typedef void (*fp_state)(EVT_TYPE evt , void* param); static const fp_state bomb2_table[MAX_STATE][MAX_EVENT] = { {setting_UP , setting_DOWN , setting_ARM , null}, {setting_UP , setting_DOWN , setting_ARM , timing_TICK} };
struct bomb_t { const fp_state const *state_table; /* the State-Table */ uint8_t state; /* the current active state */
uint8_t timeout; uint8_t code; uint8_t defuse_code; }; struct bomb bomb2= { .state_table = bomb2_table; } void bomb2_init(void) { bomb2.defuse_code = 6; // 0110 bomb2.state = SETTING; }
void bomb2_dispatch(EVT_TYPE evt , void* param) { fp_state s = NULL; if(evt > MAX_EVT) { LOG('EVT type error!'); return; } s = bomb2.state_table[bomb2.state * MAX_EVT + evt]; if(s != NULL) { s(evt , param); } } /*列出所有的狀態(tài)對應(yīng)的事件處理函數(shù)*/ void setting_UP(EVT_TYPE evt, void* param) { if(bomb1.timeout< 60) ++bomb1.timeout; bsp_display(bomb1.timeout); }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636412345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 優(yōu)點 各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。 可將狀態(tài)機進行封裝,有較好的移植性 函數(shù)指針的安全轉(zhuǎn)換 , 利用下面的特性,用戶可以擴展帶有私有屬性的狀態(tài)機和事件而使用統(tǒng)一的基礎(chǔ)狀態(tài)機接口 typedef void (*Tran)(struct StateTableTag *me, Event const *e); / || void Bomb2_setting_ARM (Bomb2 *me, Event const *e); typedef struct Bomb
{
struct StateTableTag *me; //必須為第一個成員
uint8_t private;
}
缺點 沒有實現(xiàn)進入退出動作。
一維狀態(tài)轉(zhuǎn)換表 實現(xiàn)原理: 
typedef void (*fp_action)(EVT_TYPE evt,void* param);/*轉(zhuǎn)換表基礎(chǔ)結(jié)構(gòu)*/struct tran_evt_t{ EVT_TYPE evt;uint8_t next_state;};/*狀態(tài)的描述*/struct fsm_state_t{fp_action enter_action; //進入動作fp_action exit_action; //退出動作fp_action action;
tran_evt_t* tran; //轉(zhuǎn)換表uint8_t tran_nb; //轉(zhuǎn)換表的大小const char* name;}/*狀態(tài)表本體*/#define ARRAY(x) x,sizeof(x)/sizeof(x[0])const struct fsm_state_t state_table[]={{setting_enter , setting_exit , setting_action , ARRAY(set_tran_evt),'setting' },{timing_enter , timing_exit , timing_action , ARRAY(time_tran_evt),'timing' }};/*構(gòu)建一個狀態(tài)機*/struct fsm{const struct state_t * state_table; /* the State-Table */uint8_t cur_state; /* the current active state */
uint8_t timeout;uint8_t code;uint8_t defuse_code;}bomb3;/*初始化狀態(tài)機*/void bomb3_init(void){bomb3.state_table = state_table; //指向狀態(tài)表bomb3.cur_state = setting;bomb3.defuse_code = 8; //1000}/*狀態(tài)機事件派發(fā)*/void fsm_dispatch(EVT_TYPE evt , void* param){tran_evt_t* p_tran = NULL;/*獲取當(dāng)前狀態(tài)的轉(zhuǎn)換表*/p_tran = bomb3.state_table[bomb3.cur_state]->tran;/*判斷所有可能的轉(zhuǎn)換是否與當(dāng)前觸發(fā)的事件匹配*/for(uint8_t i=0;i<x;i++){if(p_tran[i]->evt == evt)//事件會觸發(fā)轉(zhuǎn)換{if(NULL != bomb3.state_table[bomb3.cur_state].exit_action){bomb3.state_table[bomb3.cur_state].exit_action(NULL); //執(zhí)行退出動作}if(bomb3.state_table[_tran[i]->next_state].enter_action){ bomb3.state_table[_tran[i]->next_state].enter_action(NULL);//執(zhí)行進入動作}/*更新當(dāng)前狀態(tài)*/bomb3.cur_state = p_tran[i]->next_state;}else{ bomb3.state_table[bomb3.cur_state].action(evt,param);}}}/*************************************************************************
setting狀態(tài)相關(guān)
************************************************************************/void setting_enter(EVT_TYPE evt , void* param){}void setting_exit(EVT_TYPE evt , void* param){}void setting_action(EVT_TYPE evt , void* param){}tran_evt_t set_tran_evt[]={{ARM , timing},}/*timing 狀態(tài)相關(guān)*/123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 優(yōu)點 各個狀態(tài)面向用戶相對獨立,增加事件和狀態(tài)不需要去修改先前已存在的狀態(tài)事件函數(shù)。 實現(xiàn)了狀態(tài)的進入和退出 容易根據(jù)狀態(tài)躍遷圖來設(shè)計 (狀態(tài)躍遷圖列出了每個狀態(tài)的躍遷可能,也就是這里的轉(zhuǎn)換表) 實現(xiàn)靈活,可實現(xiàn)復(fù)雜邏輯,如上一次狀態(tài),增加監(jiān)護條件來減少事件的數(shù)量??蓪崿F(xiàn)非完全事件驅(qū)動
缺點
QP嵌入式實時框架特點 事件驅(qū)動型編程 面向?qū)ο?/strong> 類和單一繼承 
工具
QEP實現(xiàn)有限狀態(tài)機Fsm 實現(xiàn) 
/* qevent.h ----------------------------------------------------------------*/ typedef struct QEventTag
{
QSignal sig;
uint8_t dynamic_;
} QEvent; /* qep.h -------------------------------------------------------------------*/ typedef uint8_t QState; /* status returned from a state-handler function */ typedef QState (*QStateHandler) (void *me, QEvent const *e); /* argument list */ typedef struct QFsmTag /* Finite State Machine */ {
QStateHandler state; /* current active state */ }QFsm;
#define QFsm_ctor(me_, initial_) ((me_)->state = (initial_)) void QFsm_init (QFsm *me, QEvent const *e); void QFsm_dispatch(QFsm *me, QEvent const *e);
#define Q_RET_HANDLED ((QState)0) #define Q_RET_IGNORED ((QState)1) #define Q_RET_TRAN ((QState)2) #define Q_HANDLED() (Q_RET_HANDLED) #define Q_IGNORED() (Q_RET_IGNORED)
#define Q_TRAN(target_) (((QFsm *)me)->state = (QStateHandler) (target_),Q_RET_TRAN)
enum QReservedSignals { Q_ENTRY_SIG = 1,
Q_EXIT_SIG,
Q_INIT_SIG,
Q_USER_SIG
};
/* file qfsm_ini.c ---------------------------------------------------------*/ #include 'qep_port.h' /* the port of the QEP event processor */ #include 'qassert.h' /* embedded systems-friendly assertions */ void QFsm_init(QFsm *me, QEvent const *e)
{ (*me->state)(me, e); /* execute the top-most initial transition */ /* enter the target */ (void)(*me->state)(me , &QEP_reservedEvt_[Q_ENTRY_SIG]); } /* file qfsm_dis.c ---------------------------------------------------------*/ void QFsm_dispatch(QFsm *me, QEvent const *e) { QStateHandler s = me->state; /* save the current state */ QState r = (*s)(me, e); /* call the event handler */ if (r == Q_RET_TRAN) /* transition taken? */ {(void)(*s)(me, &QEP_reservedEvt_[Q_EXIT_SIG]); /* exit the source */(void)(*me->state)(me, &QEP_reservedEvt_[Q_ENTRY_SIG]);/*enter target*/ } }
實現(xiàn)上面定時器例子 #include 'qep_port.h' /* the port of the QEP event processor */ #include 'bsp.h' /* board support package */
enum BombSignals /* all signals for the Bomb FSM */ {
UP_SIG = Q_USER_SIG, DOWN_SIG, ARM_SIG, TICK_SIG }; typedef struct TickEvtTag
{QEvent super; /* derive from the QEvent structure */uint8_t fine_time; /* the fine 1/10 s counter */ }TickEvt;
typedef struct Bomb4Tag
{ QFsm super; /* derive from QFsm */ uint8_t timeout; /* number of seconds till explosion */uint8_t code; /* currently entered code to disarm the bomb */uint8_t defuse; /* secret defuse code to disarm the bomb */ } Bomb4;
void Bomb4_ctor (Bomb4 *me, uint8_t defuse); QState Bomb4_initial(Bomb4 *me, QEvent const *e); QState Bomb4_setting(Bomb4 *me, QEvent const *e); QState Bomb4_timing (Bomb4 *me, QEvent const *e); /*--------------------------------------------------------------------------*/ /* the initial value of the timeout */ #define INIT_TIMEOUT 10 /*..........................................................................*/ void Bomb4_ctor(Bomb4 *me, uint8_t defuse) { QFsm_ctor_(&me->super, (QStateHandler)&Bomb4_initial); me->defuse = defuse; /* the defuse code is assigned at instantiation */ } /*..........................................................................*/ QState Bomb4_initial(Bomb4 *me, QEvent const *e) { (void)e; me->timeout = INIT_TIMEOUT; return Q_TRAN(&Bomb4_setting); } /*..........................................................................*/ QState Bomb4_setting(Bomb4 *me, QEvent const *e) { switch (e->sig){ case UP_SIG:{ if (me->timeout < 60) { ++me->timeout; BSP_display(me->timeout); } return Q_HANDLED(); } case DOWN_SIG: { if (me->timeout > 1) { --me->timeout; BSP_display(me->timeout); } return Q_HANDLED(); } case ARM_SIG: { return Q_TRAN(&Bomb4_timing); /* transition to 'timing' */ } } return Q_IGNORED(); } /*..........................................................................*/ void Bomb4_timing(Bomb4 *me, QEvent const *e) { switch (e->sig) { case Q_ENTRY_SIG: { me->code = 0; /* clear the defuse code */ return Q_HANDLED(); } case UP_SIG: { me->code <<= 1; me->code |= 1; return Q_HANDLED(); } case DOWN_SIG: { me->code <<= 1; return Q_HANDLED(); } case ARM_SIG: { if (me->code == me->defuse) { return Q_TRAN(&Bomb4_setting); } return Q_HANDLED(); } case TICK_SIG: { if (((TickEvt const *)e)->fine_time == 0) { --me->timeout; BSP_display(me->timeout); if (me->timeout == 0) { BSP_boom(); /* destroy the bomb */ } } return Q_HANDLED(); } } return Q_IGNORED(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
QP 實現(xiàn)層次狀態(tài)機 Hsm簡介
初始化  初始化層次狀態(tài)機的實現(xiàn):在初始化時,用戶所選取的狀態(tài)永遠是最底層的狀態(tài),如上圖,我們在計算器開機后,應(yīng)該進入的是開始狀態(tài),這就涉及到一個問題,由最初top(頂狀態(tài))到begin 是有一條狀態(tài)切換路徑的,當(dāng)我們設(shè)置狀態(tài)為begin如何搜索這條路徑成為關(guān)鍵(知道了路徑才能正確的進入begin,要執(zhí)行路徑中過渡狀態(tài)的進入和退出事件) void QHsm_init(QHsm *me, QEvent const *e) {Q_ALLEGE((*me->state)(me, e) == Q_RET_TRAN);t = (QStateHandler)&QHsm_top; /* HSM starts in the top state */ do { /* drill into the target... */QStateHandler path[QEP_MAX_NEST_DEPTH_]; int8_t ip = (int8_t)0; /* transition entry path index */ path[0] = me->state; /* 這里的狀態(tài)為begin *//*通過執(zhí)行空信號,從底層狀態(tài)找到頂狀態(tài)的路徑*/ (void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_); while (me->state != t) { path[++ip] = me->state;(void)QEP_TRIG_(me->state, QEP_EMPTY_SIG_);}/*切換為begin*/ me->state = path[0]; /* restore the target of the initial tran. *//* 鉆到最底層的狀態(tài),執(zhí)行路徑中的所有進入事件 */ Q_ASSERT(ip < (int8_t)QEP_MAX_NEST_DEPTH_);do { /* retrace the entry path in reverse (desired) order... */ QEP_ENTER_(path[ip]); /* enter path[ip] */ } while ((--ip) >= (int8_t)0);
t = path[0]; /* current state becomes the new source */ } while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN); me->state = t;}123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627 狀態(tài)切換  /*.................................................................*/QState result(Calc *me, QEvent const *e) {switch (e->sig) {youcase ENTER_SIG:{break;}case EXIT_SIG:{break;}case C_SIG: {printf('clear'); return Q_HANDLED();}case B_SIG:{ return Q_TRAN(&begin);}}return Q_SUPER(&reday);}/*.ready為result和begin的超狀態(tài)................................................*/QState ready(Calc *me, QEvent const *e) {switch (e->sig) {case ENTER_SIG:{break;}case EXIT_SIG:{break;}case OPER_SIG:{ return Q_TRAN(&opEntered);}}return Q_SUPER(&on);}void QHsm_dispatch(QHsm *me, QEvent const *e) {QStateHandler path[QEP_MAX_NEST_DEPTH_];QStateHandler s;QStateHandler t;QState r;t = me->state; /* save the current state */do { /* process the event hierarchically... */s = me->state;r = (*s)(me, e); /* invoke state handler s */} while (r == Q_RET_SUPER); //當(dāng)前狀態(tài)不能處理事件 ,直到找到能處理事件的狀態(tài)if (r == Q_RET_TRAN) { /* transition taken? */int8_t ip = (int8_t)(-1); /* transition entry path index */int8_t iq; /* helper transition entry path index */path[0] = me->state; /* save the target of the transition */ path[1] = t;while (t != s) { /* exit current state to transition source s... */if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) {/*exit handled? */(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* find superstate of t */}t = me->state; /* me->state holds the superstate */} . . .}me->state = t; /* set new state or restore the current state */} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

t = path[0]; /* target of the transition */if (s == t) { /* (a) check source==target (transition to self) */ QEP_EXIT_(s) /* exit the source */ ip = (int8_t)0; /* enter the target */ } else { (void)QEP_TRIG_(t, QEP_EMPTY_SIG_); /* superstate of target */ t = me->state; if (s == t) { /* (b) check source==target->super */ ip = (int8_t)0; /* enter the target */ } else { (void)QEP_TRIG_(s, QEP_EMPTY_SIG_); /* superstate of src */ /* (c) check source->super==target->super */ if(me->state == t) { QEP_EXIT_(s) /* exit the source */ ip = (int8_t)0; /* enter the target */ } else { /* (d) check source->super==target */ if (me->state == path[0]) { QEP_EXIT_(s) /* exit the source */ } else { /* (e) check rest of source==target->super->super..
* and store the entry path along the way */....123456789101112131415161718192021222324252627123456789101112131415161718192021222324252627 QP實時框架的組成 
- 內(nèi)存管理 使用內(nèi)存池,對于低性能mcu,內(nèi)存極為有限,引入內(nèi)存管理主要是整個架構(gòu)中,是以事件作為主要的任務(wù)通信手段,且事件是帶參數(shù)的,可能相同類型的事件會多次觸發(fā),而事件處理完成后,需要清除事件,無法使用靜態(tài)的事件,因此是有必要為不同事件創(chuàng)建內(nèi)存池的。 對于不同塊大小的內(nèi)存池,需要考慮的是每個塊的起始地址對齊問題。在進行內(nèi)存池初始化時,我們是根據(jù)blocksize+header大小來進行劃分內(nèi)存池的。假設(shè)一個2字節(jié)的結(jié)構(gòu),如果以2來進行劃分,假設(shè)mcu 4字節(jié)對齊,那么將有一半的結(jié)構(gòu)起始地址無法對齊,這時需要為每個塊預(yù)留空間,保證每個塊的對齊。  - 事件隊列 
- 事件派發(fā) 直接事件發(fā)送 發(fā)行訂閱事件發(fā)送
- 定時事件 非有序鏈表  合作式調(diào)度器QV  可搶占式調(diào)度器QK
QP nano的簡介 完全支持層次式狀態(tài)嵌套,包括在最多4 層狀態(tài)嵌套情況下,對任何狀態(tài)轉(zhuǎn)換拓撲的可保 證的進入/ 退出動作 支持高達8 個并發(fā)執(zhí)行的,可確定的,線程安全的事件隊列的活動對象57 支持一個字節(jié)寬( 255 個信號)的信號,和一個可伸縮的參數(shù),它可被配置成0 (沒有參 數(shù)), 1 , 2 或4 字節(jié) 使用先進先出FIFO排隊策略的直接事件派發(fā)機制 每個活動對象有一個一次性時間事件(定時器),它的可配置動態(tài)范圍是0(沒有時間事 件) , 1 , 2 或4 字節(jié) 內(nèi)建的合作式vanilla 內(nèi)核 內(nèi)建的名為QK-nano 的可搶占型RTC內(nèi)核(見第六章6.3.8節(jié)) 帶有空閑回調(diào)函數(shù)的低功耗架構(gòu),用來方便的實現(xiàn)節(jié)省功耗模式。 在代碼里為流行的低端CPU架構(gòu)的C編譯器的非標準擴展進行了準備(例如,在代碼空 間分配常數(shù)對象,可重入函數(shù),等等) 基于斷言的錯誤處理策略 代碼風(fēng)格     
|