同事C代碼中的#、##把我秀了
正文
大家好,我是bug菌!
#和##對(duì)于大部分C語(yǔ)言玩得還算比較溜的朋友并不是很陌生,不過(guò)能把這兩個(gè)知識(shí)點(diǎn)游刃有余的應(yīng)用到所在代碼中的每個(gè)角落,似乎并沒(méi)有幾個(gè)人能夠做到,學(xué)的時(shí)候朗朗上口,而編碼的時(shí)候卻拋之腦后。
但是今天bug菌還是想重新介紹這兩個(gè)“兄弟”,希望大家能夠?qū)懗?quot;秀"一點(diǎn)的代碼~
1.#和##基礎(chǔ)
對(duì)于這兩個(gè)語(yǔ)法的功能都比較簡(jiǎn)單,且都是在預(yù)處理階段做一些工作 :
- #主要是將宏參數(shù)轉(zhuǎn)化為字符串
- ##主要是將兩個(gè)標(biāo)識(shí)符拼接成一個(gè)標(biāo)識(shí)符
沒(méi)點(diǎn)代碼似乎并不是那么形象 :
參考demo:
- 1#include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4//#的簡(jiǎn)單使用
- 5#define STR(str) #str
- 6
- 7//##的簡(jiǎn)單使用
- 8#define CMB(a,b) a##b
- 9
- 10int main(int argc, char *argv[]) {
- 11
- 12 int CMB(uart,1) = 5;
- 13 int CMB(uart,2) = 10;
- 14
- 15 printf("#的簡(jiǎn)單使用:\r\n");
- 16 printf("%s\r\n",STR(3.1415));
- 17 printf("%s\r\n",STR(abcd));
- 18
- 19 printf("##的簡(jiǎn)單使用:\r\n");
- 20 printf("%d\r\n",uart1);
- 21 printf("%d\r\n",uart2);
- 22
- 23 return 0;
- 24}
輸出結(jié)果:
從結(jié)果上看來(lái)似乎#僅僅只是代替了字符串的雙引號(hào),而##卻實(shí)現(xiàn)了標(biāo)識(shí)符的拼接,這樣就為編碼標(biāo)識(shí)符的處理上能夠帶來(lái)更多的可玩性。
那么,下面bug菌跟大家具體展示一下他們的常用技巧:
2.#的玩法
(1)標(biāo)識(shí)符的“字符串變量"
“#”一般結(jié)合打印語(yǔ)句組合成一個(gè)宏定義,可以方便的打印相關(guān)信息,下面給個(gè)簡(jiǎn)單的實(shí)例就明白了。
- 1#include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4//#打印調(diào)試
- 5#define DebugLogExpr(Expr) printf("%s : %d\r\n",#Expr, Expr);
- 6
- 7//私有參數(shù)訪(fǎng)問(wèn)
- 8int sFucntion(void)
- 9{
- 10 static int var = 10;
- 11 return var;
- 12}
- 13
- 14int main(int argc, char *argv[]) {
- 15
- 16 int DebugVar = 50;
- 17
- 18 DebugLogExpr(DebugVar); //直接打印變量名和變量
- 19 DebugLogExpr(100/5); //打印表達(dá)式及結(jié)果
- 20 DebugLogExpr(sFucntion()); //打印相關(guān)函數(shù)名及結(jié)果
- 21
- 22 return 1;
- 23}
輸出結(jié)果:
這樣的話(huà)就不需要總是采用雙引號(hào)來(lái)單獨(dú)書(shū)寫(xiě),同時(shí)你還可以繼續(xù)擴(kuò)展構(gòu)造更加靈活的宏。
(2)結(jié)合##進(jìn)行字符串拼接打印
前面介紹了##進(jìn)行標(biāo)識(shí)符的拼接,那么實(shí)現(xiàn)拼接標(biāo)識(shí)符轉(zhuǎn)化為字符串看來(lái)很簡(jiǎn)單吧,于是你會(huì)編寫(xiě)了如下代碼:
- 1#include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4//#的簡(jiǎn)單使用
- 5#define STR(str) #str
- 6
- 7//##的簡(jiǎn)單使用
- 8#define CMB(a,b) a##b
- 9
- 10int main(int argc, char *argv[]) {
- 11
- 12 int CMB(uart,1) = 5;
- 13
- 14 printf("%s\r\n",STR(CMB(uart,1)));
- 15
- 16 return 0;
- 17}
暗自歡喜的編譯著,然而卻得到了如下結(jié)果:
得到的并不是拼接以后你想要的uart1,難道不能這么玩?當(dāng)然不是,不然也不會(huì)在這里拿出來(lái)說(shuō) 。
首先要知道原因 : 進(jìn)行宏定義嵌套的情況,#或者##僅在當(dāng)前宏有效,嵌套宏中不會(huì)再次展開(kāi),既然當(dāng)前宏無(wú)法展開(kāi),那么我只能再加一級(jí)宏定義作為轉(zhuǎn)換宏進(jìn)行展開(kāi),看能不能解決該問(wèn)題:
- 1 #include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4//#的簡(jiǎn)單使用
- 5#define STR(str) #str
- 6
- 7//##的簡(jiǎn)單使用
- 8#define CMB(a,b) a##b
- 9
- 10#define STR_CON(str) STR(str) //轉(zhuǎn)換宏
- 11
- 12int main(int argc, char *argv[]) {
- 13
- 14 int CMB(uart,1) = 5;
- 15
- 16 printf("%s\r\n",STR_CON(CMB(uart,1)));
- 17
- 18 return 0;
- 19}
此時(shí)輸出的結(jié)果符合我們的預(yù)期:
首先進(jìn)行第一層轉(zhuǎn)換宏替換處理掉##拼接符得到str(uart1),然后進(jìn)行字符串轉(zhuǎn)換符的處理為uart1字符串打印輸出,當(dāng)然以后你會(huì)遇到一些復(fù)雜的,不過(guò)要訣就是宏替換只會(huì)處理當(dāng)前的#或者##,否則就需要增加轉(zhuǎn)換宏提前進(jìn)行宏替換展開(kāi)。
所以采用##拼接出來(lái)的標(biāo)識(shí)符想要打印輸出的話(huà),使用#進(jìn)行轉(zhuǎn)換是最直接、方便的。
3.##的玩法
##拼接符的玩法有點(diǎn)多,甚至有些還比較繞,當(dāng)然如果你游刃有余的話(huà),這對(duì)于重構(gòu)代碼是一把“ 利器 ”。
(1)在結(jié)構(gòu)體定義中的妙用
下面是bug菌經(jīng)常在項(xiàng)目代碼中用到的##結(jié)構(gòu)體定義法,也是非常多開(kāi)源代碼中慣用的做法,相比常規(guī)的結(jié)構(gòu)體定義法,確實(shí)省去很多重復(fù)的代碼。
比如下面的參考代碼 :
- 1#include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4#define DF_STRUCT(name) typedef struct tag##name name;\
- 5 struct tag##name
- 6
- 7DF_STRUCT(DevManage)
- 8{
- 9 int index; //索引
- 10 int Access; //權(quán)限
- 11 //...
- 12};
- 13
- 14int main(int argc, char *argv[]) {
- 15
- 16 DevManage stDevManage;
- 17
- 18 stDevManage.index = 1;
- 19 stDevManage.Access = 666;
- 20
- 21 printf("Dev Index :%d\n",stDevManage.index );
- 22 printf("Dev Access:%d\n",stDevManage.Access );
- 23
- 24 return 1;
- 25}
(2)統(tǒng)一宏替換
拼接標(biāo)識(shí)符意味著符號(hào)的粒度更高,而這碎片化的符號(hào)進(jìn)行有效的管理,就可以使得符號(hào)更加具有通用性和靈活性。
其實(shí)這種思想跟我們代碼模塊話(huà)是同樣的道理。
來(lái)首先我們用一個(gè)兩層拼接體驗(yàn)一下:
- 1#include <stdio.h>
- 2#include <stdlib.h>
- 3
- 4//假如這是stm32庫(kù)中的宏
- 5#define GPIO_Pin_0 ((int)0x0001) /*!< Pin 0 selected */
- 6#define GPIO_Pin_1 ((int)0x0002) /*!< Pin 1 selected */
- 7#define GPIO_Pin_2 ((int)0x0004) /*!< Pin 2 selected */
- 8#define GPIO_Pin_3 ((int)0x0008) /*!< Pin 3 selected */
- 9
- 10#define USART1 ((int *) 0x1000)
- 11#define USART2 ((int *) 0x2000)
- 12
- 13
- 14//拼接變量
- 15#define UARTX 1
- 16
- 17//最終的組合標(biāo)識(shí)符
- 18#define UART1_CORE USART1
- 19#define UART1_RX GPIO_Pin_0
- 20#define UART1_TX GPIO_Pin_1
- 21
- 22#define UART2_CORE USART2
- 23#define UART2_RX GPIO_Pin_2
- 24#define UART2_TX GPIO_Pin_3
- 25
- 26//拼接過(guò)程
- 27#define _UARTX_CORE(uartx) UART##uartx##_CORE
- 28#define UARTX_CORE(uartx) _UARTX_CORE(uartx)
- 29
- 30
- 31#define _UARTX_RX(uartx) UART##uartx##_RX
- 32#define UARTX_RX(uartx) _UARTX_RX(uartx)
- 33
- 34#define _UARTX_TX(uartx) UART##uartx##_TX
- 35#define UARTX_TX(uartx) _UARTX_TX(uartx)
- 36
- 37
- 38int main(int argc, char *argv[]) {
- 39
- 40 //組合標(biāo)識(shí)符的使用
- 41 printf("0x%x\n",UARTX_CORE(UARTX));
- 42 printf("0x%x\n",UARTX_RX(UARTX));
- 43 printf("0x%x\n",UARTX_TX(UARTX));
- 44
- 45 return 1;
- 46}
編寫(xiě)的思路bug菌在代碼中跟大家都標(biāo)注了,相信大家一眼就能看懂,似乎并沒(méi)有想象中那么難。
而在前面介紹##的基礎(chǔ)知識(shí)提過(guò),只要轉(zhuǎn)換宏寫(xiě)得夠多,你可以一層套一層,最終獲得你想要的標(biāo)識(shí)符,達(dá)到修改一個(gè)簡(jiǎn)單的宏即可替換一整套宏的效果。
所以關(guān)鍵還是你要清晰的把拼接變量找出來(lái),bug菌這里僅展示了一個(gè)拼接變量,當(dāng)然多個(gè)也是同樣沒(méi)有問(wèn)題的,跟我們函數(shù)傳遞參數(shù)一樣,不過(guò)這樣也會(huì)增加整個(gè)替換的復(fù)雜度,合理利用即可~
最后
好了,今天的內(nèi)容就分享到這里,我仍然是我,一直沒(méi)變,覺(jué)得有所收獲,記得點(diǎn)個(gè)贊。