日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 嵌入式大雜燴
[導讀]一文了解宏的高級用法。

使用switch-case/if-else

對于條件/分支處理的程序設計,我們慣性地會選擇switch-case或者if-else,這也是C語言老師當初教的。以下,我們用一個播放器的例子來說明,要實現(xiàn)的功能如下:

  1. 收到用戶操作播放器命令請求,如“播放”、“暫?!钡龋绦蛞獙γ钭鲄^(qū)分;

  2. 針對不同的命令請求,作相應的處理;

  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;
?}

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

但是,我不敢更改這個祖?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;
? ? }
?}

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

解放switch-case/if-else

腦子里想來想去,度娘上翻來翻去,于是定義了個結構體:

?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;
? ? ? ? }
? ? }
?}

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

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

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

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

使用X-MACRO

你聽說過X-MACRO么?聽過沒聽過都沒關系,來,我們一起耍起來!

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

X-MACRO是一種可靠維護代碼或數(shù)據(jù)的并行列表的技術,其相應項必須以相同的順序出現(xiàn)。它們在至少某些列表無法通過索引組成的地方(例如編譯時)最有用。此類列表的示例尤其包括數(shù)組的初始化,枚舉常量和函數(shù)原型的聲明,語句序列和切換臂的生成等。X-MACRO的使用可以追溯到1960年代。它在現(xiàn)代C和C ++編程語言中仍然有用。

X-MACRO應用程序包括兩部分:

  1. 列表元素的定義。

  2. 擴展列表以生成聲明或語句的片段。

該列表由一個宏或頭文件(名為LIST)定義,該文件本身不生成任何代碼,而僅由一系列調(diào)用宏(通常稱為“ X”)與元素的數(shù)據(jù)組成。LIST的每個擴展都在X定義之前加上一個list元素的語法。LIST的調(diào)用會為列表中的每個元素擴展X。

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

對于MACRO有幾個明顯的特征:

  1. MACRO實際上就是做替換工作;

  2. 宏定義的替換工作是在編譯前進行的,即預編譯;

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

我們就用這幾個特征把MACRO耍到牛X起來!

?#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);

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

于是可以定義一個這樣的宏:

?#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;

預編譯后,這實際上就是這樣的:

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

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

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

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

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

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

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

這不僅提高了效率,還不用擔心命令的順序問題。

這種X-MACRO的用法對分支結構,特別是消息命令的處理特別的方便高效。

以下附上該案例的完整測試源碼:

?#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;
?}

留個作業(yè)題:

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

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

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

?}tStruct;

tStruct s;

?
?unsigned char data[100];


最后

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

猜你喜歡:

干貨 | 結構體、聯(lián)合體嵌套使用的一些實用操作

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


1024G 嵌入式資源大放送!包括但不限于C/C++、單片機、Linux等。在公眾號聊天界面回復1024,即可免費獲??!


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

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

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅(qū)動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅(qū)動性能的關鍵。

關鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質(zhì)量和效率直接關系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅(qū)動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關鍵字: LED 驅(qū)動電源 開關電源

LED驅(qū)動電源是把電源供應轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅(qū)動電源
關閉