整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          不同視角理解嵌入式開發(fā)中的“數(shù)據(jù)結(jié)構(gòu)”~

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          【說在前面的話】

          2022年了,想必已經(jīng)不會(huì)有人對(duì)嵌入式開發(fā)中“數(shù)據(jù)結(jié)構(gòu)(Data )”的作用產(chǎn)生疑問了吧?無論你是否心存疑惑,本文都將給你一個(gè)完全不同的視角。

          每每說起數(shù)據(jù)結(jié)構(gòu),很多人腦海里復(fù)現(xiàn)的一定是以下的內(nèi)容:

          數(shù)據(jù)結(jié)構(gòu)其實(shí)不是一個(gè)高大上的名詞,它意外的非常樸實(shí)——你也許每天都在用。作為一個(gè)新坑,我將在【非常C結(jié)構(gòu)】系列文章中為大家分享很多嵌入式開發(fā)中很多“非常”而又“好用”的數(shù)據(jù)結(jié)構(gòu)。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          【人人都可以學(xué)會(huì)的“表格”】

          你不必學(xué)過所謂的“關(guān)系數(shù)據(jù)庫”也可以理解“表格(Table)”這種數(shù)據(jù)結(jié)構(gòu)的本質(zhì)含義。

          在C語言環(huán)境中,表格的本質(zhì)就是結(jié)構(gòu)體數(shù)組,即:由結(jié)構(gòu)體組成的數(shù)組。這里:

          在嵌入式系統(tǒng)中,表格具有以下特點(diǎn):

          如果一個(gè)需求能夠1)接受上述的特點(diǎn);或者2)本身就具有上述特點(diǎn);或者3)部分內(nèi)容經(jīng)過改造后可以接受上述特點(diǎn)——那么,就可以使用表格來保存數(shù)據(jù)了。

          一個(gè)典型的例子就是:交互菜單。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          很容易看到,每一級(jí)菜單本質(zhì)上都“可以”是一個(gè)表格。

          雖然在很多UI設(shè)計(jì)工具中(比如LVGL),菜單的內(nèi)容是在運(yùn)行時(shí)刻動(dòng)態(tài)生成的(用鏈表來實(shí)現(xiàn)),但在嵌入式系統(tǒng)中,動(dòng)態(tài)生成表格本身并不是一個(gè)“必須使用”的特性,相反,由于產(chǎn)品很多時(shí)候功能固定——菜單的內(nèi)容也是固定的,因此完全沒有必要在運(yùn)行時(shí)刻進(jìn)行動(dòng)態(tài)生成——這就滿足了表格的“在編譯時(shí)刻初始化”的要求。

          采用表格的形式來保存菜單,就獲得了在ROM中保存數(shù)據(jù)、減少RAM消耗的的優(yōu)勢(shì)。同時(shí),數(shù)組的訪問形式又進(jìn)一步簡(jiǎn)化了用戶代碼。

          另外一個(gè)常見用到表格的例子是消息地圖(Message Map),它在通信協(xié)議棧解析類的應(yīng)用中非常常見,在很多結(jié)構(gòu)緊湊功能復(fù)雜的中也充當(dāng)著重要的角色。

          如果你較真起來,菜單也不過消息地圖的一種。表格不是實(shí)現(xiàn)消息地圖的唯一方式,但卻是最簡(jiǎn)單、最常用、數(shù)據(jù)存儲(chǔ)密度最高的形式。在后續(xù)的例子中,我們就以“消息地圖”為例,深入聊聊表格的使用和優(yōu)化。

          【表格的定義】

          一般來說,表格由兩部分構(gòu)成:

          因此,表格的定義也分為兩個(gè)部分:

          記錄的定義一般格式如下:

          typedef?struct?<表格名稱>_item_t  <表格名稱>_item_t;
          struct?<表格名稱>_item_t {????//?每條記錄中的內(nèi)容};

          這里,第一行的typedef所在行的作用是“前置聲明”;struct所在行的作用是定義結(jié)構(gòu)體的實(shí)際內(nèi)容。雖然我們完全可以將“前置聲明”和“結(jié)構(gòu)體定義”合二為一,寫作:

          typedef?struct <表格名稱>_item_t {    // 每條記錄中的內(nèi)容} <表格名稱>_item_t;

          但基于以下原因,我們還是推薦大家堅(jiān)持第一種寫法:

          以消息地圖為例,一個(gè)常見的記錄結(jié)構(gòu)體定義如下:

          typedef?struct?msg_item_t msg_item_t;
          struct?msg_item_t { uint8_t chID; //!< 指令????uint8_t?chAccess;?????????????//!????uint16_t hwValidDataSize; //!< 數(shù)據(jù)長(zhǎng)度要求????bool?(*fnHandler)(msg_item_t?*ptMSG,??????? void?*pData,???????????????????????uint_fast16_t?hwSize);};

          在這個(gè)例子中,我們腦補(bǔ)了一個(gè)通信指令系統(tǒng),當(dāng)我們通過通信前端進(jìn)行數(shù)據(jù)幀解析后,獲得了以下的內(nèi)容:

          為了方便指令解析,我們也需要有針對(duì)性的來設(shè)計(jì)每一條指令的內(nèi)容,因此,我們加入了chID來存儲(chǔ)指令碼;并加入了函數(shù)指針來為當(dāng)前指令綁定一個(gè)處理函數(shù);考慮到每條指令所需的最小有效數(shù)據(jù)長(zhǎng)度是已知的,因此,我們通過來記錄這一信息,以便進(jìn)行信息檢索時(shí)快速的做出判斷。具體如何使用,我們后面再說。

          對(duì)表格來說,容器是所有記錄的容身之所,可以簡(jiǎn)單,但不可以缺席。最簡(jiǎn)單的容器就是數(shù)組,例如:

          const?msg_item_t?c_tMSGTable[20];

          這里,類型的數(shù)組就是表格的容器,而且我們手動(dòng)規(guī)定了數(shù)組中元素的個(gè)數(shù)。實(shí)踐中,我們通常不會(huì)像這樣手動(dòng)的“限定”表格中元素的個(gè)數(shù),而是直接“偷懶”——埋頭初始化數(shù)組,然后讓編譯器替我們?nèi)?shù)數(shù)——根據(jù)我們初始化元素的個(gè)數(shù)來確定數(shù)組的元素?cái)?shù)量,例如:

          const msg_item_t c_tMSGTable[] = {    [0] = {????????.chID?=?0,????????.fnHandler = NULL,    },    [1] = {        ...    },    ...};

          上述寫法是C99語法,不熟悉的小伙伴可以再去翻翻語法書哦。說句題外話,2022年了,連頑固不化的Linux都擁抱C11了,不要再抱著C89規(guī)范不放了,起碼用個(gè)C99沒問題的。

          上面寫法的好處主要是方便我們偷懶,減少不必要的“數(shù)數(shù)”過程。那么,我們要如何知道一個(gè)表格中數(shù)組究竟有多少個(gè)元素呢?別慌,我們有sizeof():

          #ifndef dimof#???dimof(__array)     (sizeof(__array)/sizeof(__array[0]))#endif

          這個(gè)語法糖dimof()可不是我發(fā)明的,不信你問Linux。它的原理很簡(jiǎn)單,當(dāng)我們把數(shù)組名稱傳給dimof()時(shí),它會(huì):

          通過 sizeof() 來獲取整個(gè)目標(biāo)數(shù)組的字節(jié)尺寸;

          通過 sizeof([0]) 來獲取數(shù)組第一個(gè)元素的字節(jié)尺寸——也就是數(shù)組元素的尺寸;

          通過除法獲取數(shù)組中元素的個(gè)數(shù)。

          【表格的訪問(遍歷)】

          由于表格的本質(zhì)是結(jié)構(gòu)體數(shù)組,因此,針對(duì)表格最常見的操作就是遍歷(搜索)了。還以前面消息地圖為例子:

          static?volatile?uint8_t?s_chCurrentAccessPermission;
          /*!?\brief?搜索消息地圖,并執(zhí)行對(duì)應(yīng)的處理程序?*!?\retval?false??消息不存在或者消息處理函數(shù)覺得內(nèi)容無效?*! \retval true 消息得到了正確的處理?*/bool?search_msgmap(uint_fast8_t?chID,?????????????????? void *pData,?????????????????? uint_fast16_t hwSize){????for?(int?n?=?0;?n?????????msg_item_t *ptItem = &c_tMSGTable[n];????????if?(chID?!=?ptItem->chID) {???????? continue;????????}????????if?(!(ptItem->chAccess & s_chCurrentAccessPermission)) {????????????continue;??//!????????}????????if?(hwSize < ptItem->hwSize) {????????????continue; //!< 數(shù)據(jù)太小了????????}????????if (NULL == ptItem->fnHandler) {????????????continue;??//!????????}????????????????//! 調(diào)用消息處理函數(shù)????????return?ptItem->fnHandler(ptItem,?pData,?hwSize);????}????????return?false;???//!}

          別看這個(gè)函數(shù)“很有料”的樣子,其本質(zhì)其實(shí)特別簡(jiǎn)單:

          其實(shí)上述代碼隱藏了一個(gè)特性:就是這個(gè)例子中的消息地圖中允許出現(xiàn)chID相同的消息的——這里的技巧是:對(duì)同一個(gè)chID值的消息,我們可以針對(duì)不同的訪問權(quán)限(值)來提供不同的處理函數(shù)。比如,通信系統(tǒng)中,我們可以設(shè)計(jì)多種權(quán)限和模式,比如:只讀模式、只寫模式、安全模式等等。不同模式對(duì)應(yīng)不同的值。這樣,對(duì)哪怕同樣的指令,我們也可以根據(jù)當(dāng)前模式的不同提供不同的處理函數(shù)——這只是一種思路,供大家參考。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          【由多實(shí)例引入的問題】

          前面的例子為我們展示表格使用的大體細(xì)節(jié),對(duì)很多嵌入式應(yīng)用場(chǎng)景來說,已經(jīng)完全夠用了。但愛思考的小伙伴一定已經(jīng)發(fā)現(xiàn)了問題:

          如果我的系統(tǒng)中有多個(gè)消息地圖(每個(gè)消息地圖中消息數(shù)量是不同的),我改怎么復(fù)用代碼呢?

          指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          為了照顧還一臉懵逼的小伙伴,我把這個(gè)問題給大家翻譯翻譯:

          簡(jiǎn)而言之,()現(xiàn)在跟某一個(gè)消息地圖(數(shù)組)綁定死了,如果要讓它支持其它的消息地圖(其它數(shù)組),就必須想辦法將其與特定的數(shù)組解耦,換句話說,在使用()的時(shí)候,要提供目標(biāo)的消息地圖的指針,以及消息地圖中元素的個(gè)數(shù)。

          一個(gè)頭疼醫(yī)頭腳疼醫(yī)腳的修改方案呼之欲出:

          bool?search_msgmap(msg_item_t?*ptMSGTable,                   uint_fast16_t hwCount,                   uint_fast8_t chID,                   void *pData,                   uint_fast16_t hwSize){    for (int n = 0; n < hwCount; n++) {        msg_item_t *ptItem = &ptMSGTable[n];        if (chID != ptItem->chID) {            continue;        }        ...
          //! 調(diào)用消息處理函數(shù) return ptItem->fnHandler(ptItem, pData, hwSize); }
          return false; //!< 沒找到對(duì)應(yīng)的消息}

          假設(shè)我們有多個(gè)消息地圖,對(duì)應(yīng)不同的工作模式:

          const?msg_item_t?c_tMSGTableUserMode[] = {    ...};const msg_item_t c_tMSGTableSetupMode[] = {    ...};
          const msg_item_t c_tMSGTableDebugMode[] = { ...};
          const msg_item_t c_tMSGTableFactoryMode[] = { ...};

          在使用的時(shí)候,可以這樣:

          typedef enum?{????USER_MODE?=?0,????//!????SETUP_MODE,???????//!????DEBUG_MODE,???????//!????FACTORY_MODE,?????//!}?comm_mode_t;
          bool?frame_process_backend(comm_mode_t tWorkMode,?????????????????????????? uint_fast8_t chID,?????????????????????????? void *pData,?????????????????????????? uint_fast16_t hwSize){????bool?bHandled = false; switch (tWorkMode) { case USER_MODE:????????????bHandled = search_msgmap(???????????? c_tMSGTableUserMode, ???????????? dimof(c_tMSGTableUserMode),???????????? chID,???????????? pData,??????????????????????????hwSize);????????????break;???????? case SETUP_MODE: bHandled = search_msgmap( c_tMSGTableSetupMode, dimof(c_tMSGTableUserMode), chID, pData, hwSize); break;?????????... }
          return bHandled;}

          看起來很不錯(cuò),對(duì)吧?非也非也!早得很呢。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效

          【表格定義的完全體】

          前面我們說過,表格的定義分兩個(gè)部分:

          其中,關(guān)于容器的定義,我們說過,數(shù)組是容器的最簡(jiǎn)單形式。那么容器定義的完全體是怎樣的呢?

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效

          “還是結(jié)構(gòu)體”!

          是的,表格條目的本質(zhì)是結(jié)構(gòu)體,表格容器的本質(zhì)也是一個(gè)結(jié)構(gòu)體:

          typedef struct <表格名稱>_item_t  <表格名稱>_item_t;
          struct <表格名稱>_item_t { // 每條記錄中的內(nèi)容};
          typedef struct <表格名稱>_t <表格名稱>_t;
          struct <表格名稱>_t {????uint16_t?hwItemSize;????uint16_t?hwCount;????<表格名稱>_item_t *ptItems;};

          容易發(fā)現(xiàn),這里表格容器被定義成了一個(gè)叫做 _t 的結(jié)構(gòu)體,其中包含了三個(gè)至關(guān)重要的元素:

          這個(gè)其實(shí)是來湊數(shù)的,因?yàn)?2位系統(tǒng)中指針4字節(jié)對(duì)齊的緣故,2字節(jié)的hwCount橫豎會(huì)產(chǎn)生2字節(jié)的氣泡。不理解這一點(diǎn)的小伙伴,可以參考文章《》

          還是以前面消息地圖為例,我們來看看新的容器應(yīng)該如何定義和使用:

          typedef?struct?msg_item_t?msg_item_t;
          struct msg_item_t { uint8_t chID; //!< 指令 uint8_t chAccess; //!< 訪問權(quán)限檢測(cè) uint16_t hwValidDataSize; //!< 數(shù)據(jù)長(zhǎng)度要求 bool (*fnHandler)(msg_item_t *ptMSG, void *pData, uint_fast16_t hwSize);};
          typedef?struct?msgmap_t?msgmap_t;
          struct msgmap_t { uint16_t hwItemSize; uint16_t hwCount;????msg_item_t *ptItems;};
          const msg_item_t c_tMSGTableUserMode[] = { ...};
          const?msgmap_t?c_tMSGMapUserMode = {????.hwItemSize?=?sizeof(msg_item_t),????.hwCount?=?dimof(c_tMSGTableUserMode),????.ptItems = c_tMSGTableUserMode,};

          既然有了定義,()也要做相應(yīng)的更新:

          bool?search_msgmap(msgmap_t?*ptMSGMap,                   uint_fast8_t chID,                   void *pData,                   uint_fast16_t hwSize){    for (int n = 0; n < ptMSGMap->hwCount; n++) {        msg_item_t *ptItem = &(ptMSGMap->ptItems[n]);        if (chID != ptItem->chID) {            continue;        }        ...
          //! 調(diào)用消息處理函數(shù) return ptItem->fnHandler(ptItem, pData, hwSize); }
          return false; //!< 沒找到對(duì)應(yīng)的消息}

          看到這里,相信很多小伙伴內(nèi)心是毫無波瀾的……

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          “是的……是稍微優(yōu)雅一點(diǎn)……然后呢?”

          “就這?。烤瓦@?!”

          別急,下面才是見證奇跡的時(shí)刻。

          【要優(yōu)雅……】

          在前面的例子中,我們注意到表格的初始化是分兩部分進(jìn)行的:

          const msg_item_t c_tMSGTableUserMode[] = {    [0] = {        .chID = 0,        .fnHandler = NULL,    },    [1] = {        ...    },    ...};
          const?msgmap_t?c_tMSGMapUserMode = {????.hwItemSize?=?sizeof(msg_item_t),????.hwCount?=?dimof(c_tMSGTableUserMode),????.ptItems = c_tMSGTableUserMode,};

          那么,我們可不可以把它們合二為一呢?這樣:

          要做到這一點(diǎn),我們可以使用一個(gè)類似“匿名數(shù)組”的功能:

          我們想象中的樣子:

          const msgmap_t c_tMSGMapUserMode = {    .hwItemSize = sizeof(msg_item_t),    .hwCount = dimof(c_tMSGTableUserMode),    .ptItems = const msg_item_t c_tMSGTableUserMode[] = {          [0] = {              .chID = 0,              .fnHandler = NULL,          },          [1] = {              ...          },          ...      },};

          使用“匿名數(shù)組”后的樣子(也就是刪除數(shù)組名稱后的樣子):

          const msgmap_t c_tMSGMapUserMode = {    .hwItemSize = sizeof(msg_item_t),    .hwCount = dimof(c_tMSGTableUserMode),????.ptItems?=?(msg_item_t?[]){          [0] = {              .chID = 0,              .fnHandler = NULL,          },          [1] = {              ...          },          ...      },};

          指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          其實(shí),這不是什么“黑魔法”,而是一個(gè)廣為使用的GNU擴(kuò)展,被稱為“復(fù)合式描述( literal)”,本質(zhì)上就是一種以“省略”數(shù)組或結(jié)構(gòu)體名稱的方式來初始化數(shù)組或結(jié)構(gòu)體的語法結(jié)構(gòu)。具體語法介紹,小伙伴們可以參考這篇文章《》。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效

          眼尖的小伙伴也許已經(jīng)發(fā)現(xiàn)了問題:既然我們省略了變量名,那么如何通過 dimof() 來獲取數(shù)組元素的個(gè)數(shù)呢?

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效

          少俠好眼力!

          解決方法不僅有,而且簡(jiǎn)單粗暴:

          const msgmap_t c_tMSGMapUserMode = {    .hwItemSize = sizeof(msg_item_t),
          .hwCount = dimof((msg_item_t []){ [0] = { .chID = 0, .fnHandler = NULL, }, [1] = { ... }, ...??????}),
          ????.ptItems?=?(msg_item_t?[]){ [0] = { .chID = 0, .fnHandler = NULL, }, [1] = { ... }, ... },};

          指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          所以說?……

          為了優(yōu)雅的初始化……

          我們要把同樣的內(nèi)容寫兩次?!!

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          手寫的確挺愚蠢,但宏可以?。?/p>

          #define __impl_table(__item_type, ...)                   \????.ptItems?=?(__item_type?[])?{??????????????????????? \        __VA_ARGS__                                      \    },                                                   \????.hwCount?=?sizeof((__item_type?[])?{?__VA_ARGS__?})??\             / sizeof(__item_type),                      \    .hwItemSize = sizeof(__item_type)
          #define?impl_table(__item_type,?...)???? \ __impl_table(__item_type,?__VA_ARGS__)

          借助上面的語法糖,我們可以輕松的將整個(gè)表格的初始化變得簡(jiǎn)單優(yōu)雅:

          const msgmap_t c_tMSGMapUserMode = {    impl_table(msg_item_t,           [0] = {              .chID = 0,              .fnHandler = NULL,          },          [1] = {              ...          },          ...    ),};

          這下舒服了吧?

          【禁止套娃……】

          還記得前面多實(shí)例的例子吧?

          const msg_item_t c_tMSGTableUserMode[] = {    ...};const msg_item_t c_tMSGTableSetupMode[] = {    ...};
          const msg_item_t c_tMSGTableDebugMode[] = { ...};
          const msg_item_t c_tMSGTableFactoryMode[] = { ...};

          現(xiàn)在當(dāng)然就要改為如下的形式了:

          const msgmap_t c_tMSGMapUserMode = {    impl_table(msg_item_t,         ...????),};
          const msgmap_t c_tMSGMapSetupMode = { impl_table(msg_item_t, ... ),};
          const msgmap_t c_tMSGMapDebugMode = { impl_table(msg_item_t, ... ),};
          const msgmap_t c_tMSGMapFactoryMode = { impl_table(msg_item_t, ... ),};

          但……它們不都是類型的么?為啥不做一個(gè)數(shù)組呢?

          typedef enum {    USER_MODE = 0,    //!< 普通的用戶模式    SETUP_MODE,       //!< 出廠后的安裝模式    DEBUG_MODE,       //!< 工程師專用的調(diào)試模式    FACTORY_MODE,     //!< 最高權(quán)限的工廠模式} comm_mode_t;
          const?msgmap_t?c_tMSGMap[] = { [USER_MODE] = { impl_table(msg_item_t, ... ), }, [SETUP_MODE] = { impl_table(msg_item_t, ... ), }, [DEBUG_MODE] = { impl_table(msg_item_t, ... ), }, [FACTORY_MODE] = { impl_table(msg_item_t, ... ), },};

          是不是有點(diǎn)意思了?再進(jìn)一步,我們完全可以做一個(gè)新的表格,表格的元素就是 呀?

          typedef?struct?cmd_modes_t?cmd_modes_t;
          struct cmd_modes_t { uint16_t hwItemSize; uint16_t hwCount; msgmap_t *ptItems;};

          然后就可以開始套娃咯:

          const?cmd_modes_t?c_tCMDModes?=?{????impl_table(msgmap_t,????    [USER_MODE] = {            impl_table(msg_item_t,                 [0] = {                    .chID = 0,                    .fnHandler = NULL,                },                [1] = {                    ...                },                ...            ),        },        [SETUP_MODE] = {            impl_table(msg_item_t,                 ...            ),        },        [DEBUG_MODE] = {            impl_table(msg_item_t,                 ...            ),        },        [FACTORY_MODE] = {            impl_table(msg_item_t,                 ...            ),        },????),};

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效

          【差異化……】

          在前面的例子中,我們可以根據(jù)新的定義方式更新函數(shù)d()函數(shù):


          extern const cmd_modes_t c_tCMDModes;
          bool frame_process_backend(comm_mode_t tWorkMode, uint_fast8_t chID, void *pData, uint_fast16_t hwSize){ bool bHandled = false;
          ????if?(tWorkMode?> FACTORY_MODE) {???? return false;????}????????return search_msgmap( &(c_tCMDModes.ptItems[tWorkMode]),? chID, pData,??????????????????????????hwSize);}

          是不是特別優(yōu)雅?

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          把容器定義成結(jié)構(gòu)體還有一個(gè)好處,就是可以給表格更多的差異化,這意味著,除了條目數(shù)組相關(guān)的內(nèi)容外,我們還可以放入其它東西,比如:

          現(xiàn)有的d()為每一個(gè)消息地圖()都使用相同的處理函數(shù)(),這顯然缺乏差異化的可能性。如果每個(gè)消息地圖都有可能有自己的特殊處理函數(shù)怎么辦呢?

          為了實(shí)現(xiàn)這一功能,我們可以對(duì) 進(jìn)行擴(kuò)展:

          typedef struct msgmap_t msgmap_t;
          struct msgmap_t { uint16_t hwItemSize; uint16_t hwCount; msg_item_t *ptItems;????bool?(*fnHandler)(msgmap_t?*ptMSGMap, uint_fast8_t chID, void *pData,??????????????????????uint_fast16_t?hwSize);};

          則初始化的時(shí)候,我們就可以給每個(gè)消息地圖指定一個(gè)不同的處理函數(shù):

          extern     bool?msgmap_user_mode_handler(msgmap_t?*ptMSGMap,                      uint_fast8_t chID,                      void *pData,                      uint_fast16_t hwSize);
          extern bool msgmap_debug_mode_handler(msgmap_t *ptMSGMap, uint_fast8_t chID, void *pData, uint_fast16_t hwSize);
          const cmd_modes_t c_tCMDModes = { impl_table(msgmap_t, [USER_MODE] = { impl_table(msg_item_t, ????????????????... ), .fnHandler = &msgmap_user_mode_handler, }, [SETUP_MODE] = { impl_table(msg_item_t, ... ),????????????.fnHandler?=?NULL;?//! }, [DEBUG_MODE] = { impl_table(msg_item_t, ... ), .fnHandler = &msgmap_debug_mode_handler, }, [FACTORY_MODE] = { impl_table(msg_item_t, ... ),????????????//.fnHandler = NULL 什么都不寫,就是NULL(0) }, ),};

          此時(shí),我們?cè)俑耫()函數(shù),讓上述差異化功能成為可能:


          bool frame_process_backend(comm_mode_t tWorkMode, uint_fast8_t chID, void *pData, uint_fast16_t hwSize){ bool bHandled = false;????msgmap_t?*ptMSGMap?= c_tCMDModes.ptItems[tWorkMode];????if?(tWorkMode?> FACTORY_MODE) {???? return false;????}????????//!?調(diào)用每個(gè)消息地圖自己的處理程序????if (NULL != ptMSGMap->fnHandler) {?????????return?ptMSGMap->fnHandler(ptMSGMap,? chID, pData, hwSize);????}????//!?默認(rèn)的消息地圖處理程序????return search_msgmap( ptMSGMap, chID, pData, hwSize);}

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          【說在后面的話】

          啥都不說了……你們看著辦吧。我們下期再見。

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效

          安裝數(shù)據(jù)庫指定的實(shí)例名稱無效_指定的實(shí)例無效_安裝數(shù)據(jù)庫指定的實(shí)例名稱無效


          主站蜘蛛池模板: 精品一区二区三区中文字幕| 在线视频一区二区日韩国产| 波多野结衣中文字幕一区| 中文字幕一区二区精品区| 无码国产精品一区二区免费vr | 久久精品一区二区免费看| 好湿好大硬得深一点动态图91精品福利一区二区 | 国产免费无码一区二区| 亚洲AV无码一区二区三区人 | 无码人妻精一区二区三区| 精品成人一区二区三区免费视频| 亚洲毛片不卡av在线播放一区| 中文字幕aⅴ人妻一区二区| 亚洲美女一区二区三区| 日韩十八禁一区二区久久| 亚洲AV色香蕉一区二区| 精品国产一区在线观看| 久夜色精品国产一区二区三区| 亚洲高清日韩精品第一区| 乱中年女人伦av一区二区| 日韩一区二区三区无码影院| 国产一区二区三区在线免费| 无码国产精成人午夜视频一区二区 | 精品国产一区二区三区在线| 亚洲人成人一区二区三区| 无码人妻精品一区二区三区9厂| 成人免费av一区二区三区| 亚洲天堂一区二区三区| 亚洲AⅤ视频一区二区三区| 在线精品一区二区三区| 精品日本一区二区三区在线观看 | 一区二区精品在线观看| 亚洲一区二区三区久久| 亚洲国产高清在线一区二区三区 | 高清精品一区二区三区一区| 视频一区视频二区在线观看| 亚洲区精品久久一区二区三区| 日本美女一区二区三区| 无码播放一区二区三区| 国产精品女同一区二区| 日韩精品无码人妻一区二区三区|