當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式大雜燴
[導(dǎo)讀]一文了解宏的高級(jí)用法。

使用switch-case/if-else

對(duì)于條件/分支處理的程序設(shè)計(jì),我們慣性地會(huì)選擇switch-case或者if-else,這也是C語(yǔ)言老師當(dāng)初教的。以下,我們用一個(gè)播放器的例子來(lái)說(shuō)明,要實(shí)現(xiàn)的功能如下:

  1. 收到用戶操作播放器命令請(qǐng)求,如“播放”、“暫停”等,程序要對(duì)命令作區(qū)分;

  2. 針對(duì)不同的命令請(qǐng)求,作相應(yīng)的處理;

  3. 輸出必要的輔助信息。

首先,將命令定義成enum類型:

?enum
?{
? ? ?CMD_PLAY,
? ? ?CMD_PAUSE,
? ? ?CMD_STOP,
? ? ?CMD_PLAY_NEXT,
? ? ?CMD_PLAY_PREV,
?};

然后,用switch-case的分支處理:

?switch(cmd)
?{
? ? ?case CMD_PLAY:
? ? ? ? ?// handle play command
? ? ? ? ?break;
? ? ?case CMD_PAUSE:
? ? ? ? ?// handle pause command
? ? ? ? ?break;
? ? ?case CMD_STOP:
? ? ? ? ?// handle stop command
? ? ? ? ?break;
? ? ?case CMD_PLAY_NEXT:
? ? ? ? ?// handle play next command
? ? ? ? ?break;
? ? ?case CMD_PLAY_PREV:
? ? ? ? ?// handle play previous command
? ? ? ? ?break;
? ? ?default:
? ? ? ? ?break;
?}

實(shí)際上,這也沒(méi)什么毛病。但是,時(shí)間長(zhǎng)了,需求不斷變更,程序不斷迭代,這個(gè)switch-case會(huì)變得非常冗長(zhǎng)而很難維護(hù)。你不相信?我曾經(jīng)見(jiàn)到過(guò)>1000行的類似這樣的代碼。如果讓你接手維護(hù)這樣的代碼,你內(nèi)心會(huì)不會(huì)狂奔著萬(wàn)千草泥馬?

但是,我不敢更改這個(gè)祖?zhèn)鞯?/span>switch-case啊,那么小心翼翼地將這些命令處理封裝成函數(shù)。像這樣:

?#define FUNC_IN() ? printf("enter %s \r\n", __FUNCTION__)
?
?void func_cmd_play(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_pause(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_stop(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_play_next(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_play_prev(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void player_cmd_handle(int cmd, void* p)
?{
? ? ?switch(cmd)
? ? {
? ? ? ? ?case CMD_PLAY:
? ? ? ? ? ? ?func_cmd_play(p);
? ? ? ? ? ? ?break;
? ? ? ? ?case CMD_PAUSE:
? ? ? ? ? ? ?func_cmd_pause(p);
? ? ? ? ? ? ?break;
? ? ? ? ?case CMD_STOP:
? ? ? ? ? ? ?func_cmd_stop(p);
? ? ? ? ? ? ?break;
? ? ? ? ?case CMD_PLAY_NEXT:
? ? ? ? ? ? ?func_cmd_play_next(p);
? ? ? ? ? ? ?break;
? ? ? ? ?case CMD_PLAY_PREV:
? ? ? ? ? ? ?func_cmd_play_prev(p);
? ? ? ? ? ? ?break;
? ? ? ? ?default:
? ? ? ? ? ? ?break;
? ? }
?}

后來(lái),甲方還是不斷地更改需求,導(dǎo)致播放器的命令越來(lái)越多,幾十個(gè)上百個(gè)了……痛定思痛,我——要——改——革?。?/span>

解放switch-case/if-else

腦子里想來(lái)想去,度娘上翻來(lái)翻去,于是定義了個(gè)結(jié)構(gòu)體:

?typedef void(*pFunc)(void* p);
?typedef struct
?{
? ? ?tCmd cmd;
? ? ?pFunc func;
?}tPlayerStruct;
?
?tPlayerStruct player_cmd_func[] =
?{
? ? {CMD_PLAY, ? ? ? func_cmd_play) },
? ? {CMD_PAUSE, ? ? ?func_cmd_pause) },
? ? {CMD_STOP, ? ? ? func_cmd_stop) },
? ? {CMD_PLAY_NEXT, ?func_cmd_play_next) },
? ? {CMD_PLAY_PREV, ?func_cmd_play_prev) },
?};
?#define ARR_LEN(arr)sizeof(arr)/sizeof(arr[0])
?void player_cmd_handle(int cmd, void* p)
?{
? ? ?for(int i = 0; i < ARR_LEN(player_cmd_func); i++)
? ? {
? ? ? ? ?if(player_cmd_func[i].cmd == cmd && NULL != player_cmd_func[i].func)
? ? ? ? {
? ? ? ? ? ? ?player_cmd_func[i].func(p);
? ? ? ? ? ? ?break;
? ? ? ? }
? ? }
?}

咦?好像代碼簡(jiǎn)潔了不少哦,改完之后好有成就感。

身為追求卓越的程序員,我還是有點(diǎn)不滿意,可不可以不用for循環(huán),直接使用player_cmd_func[cmd].func(p);,這樣還可以免去查詢的步驟,提高效率?

想法是好的,如果上面的程序不用for循環(huán),有可能數(shù)組越界,還有如果有命令增加,順序下標(biāo)不對(duì)應(yīng)的問(wèn)題。

之前,我在《C語(yǔ)言的奇技淫巧之五》中的第50條提到過(guò)這個(gè)方法,還立了個(gè)flag,我要用MACRO寫個(gè)更高效更好的代碼!

使用X-MACRO

你聽說(shuō)過(guò)X-MACRO么?聽過(guò)沒(méi)聽過(guò)都沒(méi)關(guān)系,來(lái),我們一起耍起來(lái)!

MACRO或者說(shuō)宏定義(書上或者規(guī)范上一般講預(yù)處理)基本原因都很簡(jiǎn)單,看看就很容易學(xué)會(huì)。看起來(lái)好像也是平淡無(wú)奇,似乎沒(méi)什么大作用。但是,你可別小看它,我們將其安上個(gè)"X"就很牛逼(不知道這個(gè)是啥傳統(tǒng),對(duì)于某些函數(shù)的擴(kuò)展,喜歡在其前面或后面加個(gè)“X”,然后這個(gè)函數(shù)比之前的函數(shù)功能強(qiáng)大很多,Windows里面的Api就有這案例)。

X-MACRO是一種可靠維護(hù)代碼或數(shù)據(jù)的并行列表的技術(shù),其相應(yīng)項(xiàng)必須以相同的順序出現(xiàn)。它們?cè)谥辽倌承┝斜頍o(wú)法通過(guò)索引組成的地方(例如編譯時(shí))最有用。此類列表的示例尤其包括數(shù)組的初始化,枚舉常量和函數(shù)原型的聲明,語(yǔ)句序列和切換臂的生成等。X-MACRO的使用可以追溯到1960年代。它在現(xiàn)代C和C ++編程語(yǔ)言中仍然有用。

X-MACRO應(yīng)用程序包括兩部分:

  1. 列表元素的定義。

  2. 擴(kuò)展列表以生成聲明或語(yǔ)句的片段。

該列表由一個(gè)宏或頭文件(名為L(zhǎng)IST)定義,該文件本身不生成任何代碼,而僅由一系列調(diào)用宏(通常稱為“ X”)與元素的數(shù)據(jù)組成。LIST的每個(gè)擴(kuò)展都在X定義之前加上一個(gè)list元素的語(yǔ)法。LIST的調(diào)用會(huì)為列表中的每個(gè)元素?cái)U(kuò)展X。

好了,少扯淡,我們是實(shí)戰(zhàn)派,搞點(diǎn)有用的東西。

對(duì)于MACRO有幾個(gè)明顯的特征:

  1. MACRO實(shí)際上就是做替換工作;

  2. 宏定義的替換工作是在編譯前進(jìn)行的,即預(yù)編譯;

  3. 宏定義可以用undef取消,然后再重新反復(fù)定義。

我們就用這幾個(gè)特征把MACRO耍到牛X起來(lái)!

?#define X(a,b)a
?int x = DEF_X(1,2);
?#undef DEF_X
?#define DEF_X(a,b)b
?int y = DEF_X(1,2);

從上面可以看到,這個(gè)xy的值是不一樣的。

于是可以定義一個(gè)這樣的宏:

?#define CMD_FUNC ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
? ? ? ? ? ? ?DEF_X(CMD_PLAY, func_cmd_play) ? ? ? ? ? ? \
? ? ? ? ? ? ?DEF_X(CMD_PAUSE, func_cmd_pause) ? ? ? ? ? \
? ? ? ? ? ? ?DEF_X(CMD_STOP, func_cmd_stop) ? ? ? ? ? ? \
? ? ? ? ? ? ?DEF_X(CMD_PLAY_NEXT, func_cmd_play_next) ? \
? ? ? ? ? ? ?DEF_X(CMD_PLAY_PREV, func_cmd_play_prev) ? \

CMDenum可以這樣定義:

?typedef enum
?{
? ? ?#define DEF_X(a,b) a,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
? ? ?CMD_MAX
?}tCmd;

預(yù)編譯后,這實(shí)際上就是這樣的:

?typedef enum
?{
? ? ?CMD_PLAY, CMD_PAUSE, CMD_STOP, CMD_PLAY_NEXT, CMD_PLAY_PREV, CMD_MAX
?}tCmd;

接著,我們按這種套路定義一個(gè)函數(shù)指針數(shù)組:

?const pFunc player_funcs[] =
?{
? ? ?#define DEF_X(a,b) b,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
?};

甚至,我們可以定義一個(gè)命令的字符串,以作打印信息用:

?const char* str_cmd[] =
?{
? ? ?#define DEF_X(a,b) #a,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
?};

只要這個(gè)DEF_X(a,b)里面的ab是對(duì)應(yīng)關(guān)系正確的,CMD_FUNC后面的元素順序是所謂了,這個(gè)比前面的結(jié)構(gòu)體有天然優(yōu)勢(shì)。這樣,我們就可以直接用下標(biāo)開始操作了:

?void player_cmd_handle(tCmd cmd, void* p)
?{
? ? ?if(cmd < CMD_MAX)
? ? {
? ? ? ? ?player_funcs[cmd](p);
? ? }
? ? ?else
? ? {
? ? ? ? ?printf("Command(%d) invalid!\n", cmd);
? ? }
?}

這不僅提高了效率,還不用擔(dān)心命令的順序問(wèn)題。

這種X-MACRO的用法對(duì)分支結(jié)構(gòu),特別是消息命令的處理特別的方便高效。

以下附上該案例的完整測(cè)試源碼:

?#include 
?
?#define FUNC_IN() ? printf("enter %s \r\n", __FUNCTION__)
?
?#define CMD_FUNC ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? \
? ? ? ? ?DEF_X(CMD_PLAY, func_cmd_play) ? ? ? ? ? ? \
? ? ? ? ?DEF_X(CMD_PAUSE, func_cmd_pause) ? ? ? ? ? \
? ? ? ? ?DEF_X(CMD_STOP, func_cmd_stop) ? ? ? ? ? ? \
? ? ? ? ?DEF_X(CMD_PLAY_NEXT, func_cmd_play_next) ? \
? ? ? ? ?DEF_X(CMD_PLAY_PREV, func_cmd_play_prev) ? \
?
?typedef enum
?{
? ? ?#define DEF_X(a,b) a,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
? ? ?CMD_MAX
?}tCmd;
?
?const char* str_cmd[] =
?{
? ? ?#define DEF_X(a,b) #a,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
?};
?
?typedef void(*pFunc)(void* p);
?
?void func_cmd_play(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_pause(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_stop(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_play_next(void* p)
?{
? ? ?FUNC_IN();
?}
?
?void func_cmd_play_prev(void* p)
?{
? ? ?FUNC_IN();
?}
?
?const pFunc player_funcs[] =
?{
? ? ?#define DEF_X(a,b) b,
? ? ?CMD_FUNC
? ? ?#undef DEF_X
?};
?
?void player_cmd_handle(tCmd cmd, void* p)
?{
? ? ?if(cmd < CMD_MAX)
? ? {
? ? ? ? ?player_funcs[cmd](p);
? ? }
? ? ?else
? ? {
? ? ? ? ?printf("Command(%d) invalid!\n", cmd);
? ? }
?}
?
?int main(void)
?{
? ? ?player_cmd_handle(CMD_PAUSE, (void*)0);
? ? ?player_cmd_handle(100, (void*)0);
? ? ?return 0;
?}

留個(gè)作業(yè)題:

如何靈活地將一個(gè)結(jié)構(gòu)體的內(nèi)容系列化到一個(gè)數(shù)組中,以及如何將一個(gè)數(shù)組的內(nèi)容解系列化到結(jié)構(gòu)體中?

例如,將以下結(jié)構(gòu)體s的內(nèi)容copy到data中(別老想著memcopy哦):

?typedef struct STRUCT_DATA
?{
? ? ?int a;
? ? ?char b;
? ? ?short c;

?}tStruct;

tStruct s;

?
?unsigned char data[100];


最后

以上就是本次的分享,如果覺(jué)得文章不錯(cuò),轉(zhuǎn)發(fā)、在看,也是我們繼續(xù)更新的動(dòng)力。

猜你喜歡:

干貨 | 結(jié)構(gòu)體、聯(lián)合體嵌套使用的一些實(shí)用操作

2020年精選原創(chuàng)筆記匯總


1024G 嵌入式資源大放送!包括但不限于C/C++、單片機(jī)、Linux等。在公眾號(hào)聊天界面回復(fù)1024,即可免費(fèi)獲?。?/span>


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉