#和##對于大部分C語言玩得還算比較溜的朋友并不是很陌生,不過能把這兩個知識點游刃有余的應用到所在代碼中的每個角落,似乎并沒有幾個人能夠做到,學的時候朗朗上口,而編碼的時候卻拋之腦后。
但是今天bug菌還是想重新介紹這兩個“兄弟”,希望大家能夠寫出"秀"一點的代碼~
1#和##基礎
對于這兩個語法的功能都比較簡單,且都是在預處理階段做一些工作 :
#主要是將宏參數轉化為字符串
##主要是將兩個標識符拼接成一個標識符
沒點代碼似乎并不是那么形象 :
參考demo:
1#include2#include 3 4//#的簡單使用 5#defineSTR(str)#str 6 7//##的簡單使用 8#defineCMB(a,b)a##b 9 10intmain(intargc,char*argv[]){ 11 12intCMB(uart,1)=5; 13intCMB(uart,2)=10; 14 15printf("#的簡單使用: "); 16printf("%s ",STR(3.1415)); 17printf("%s ",STR(abcd)); 18 19printf("##的簡單使用: "); 20printf("%d ",uart1); 21printf("%d ",uart2); 22 23return0; 24}
輸出結果:
從結果上看來似乎#僅僅只是代替了字符串的雙引號,而##卻實現了標識符的拼接,這樣就為編碼標識符的處理上能夠帶來更多的可玩性。 那么,下面bug菌跟大家具體展示一下他們的常用技巧:
2#的玩法
1、標識符的“字符串變量"
“#”一般結合打印語句組合成一個宏定義,可以方便的打印相關信息,下面給個簡單的實例就明白了。
1#include2#include 3 4//#打印調試 5#defineDebugLogExpr(Expr)printf("%s:%d ",#Expr,Expr); 6 7//私有參數訪問 8intsFucntion(void) 9{ 10staticintvar=10; 11returnvar; 12} 13 14intmain(intargc,char*argv[]){ 15 16intDebugVar=50; 17 18DebugLogExpr(DebugVar);//直接打印變量名和變量 19DebugLogExpr(100/5);//打印表達式及結果 20DebugLogExpr(sFucntion());//打印相關函數名及結果 21 22return1; 23}
輸出結果:
?
這樣的話就不需要總是采用雙引號來單獨書寫,同時你還可以繼續擴展構造更加靈活的宏。
2、結合##進行字符串拼接打印
前面介紹了##進行標識符的拼接,那么實現拼接標識符轉化為字符串看來很簡單吧,于是你會編寫了如下代碼:
1#include暗自歡喜的編譯著,然而卻得到了如下結果:2#include 3 4//#的簡單使用 5#defineSTR(str)#str 6 7//##的簡單使用 8#defineCMB(a,b)a##b 9 10intmain(intargc,char*argv[]){ 11 12intCMB(uart,1)=5; 13 14printf("%s ",STR(CMB(uart,1))); 15 16return0; 17}
![de595b1a-9301-11ee-939d-92fbcf53809c.png](https://file1.elecfans.com/web2/M00/B2/95/wKgaomVufLCAFyGOAAANbEMGLKk777.png)
得到的并不是拼接以后你想要的uart1,難道不能這么玩?當然不是,不然也不會在這里拿出來說 。 首先要知道原因 : 進行宏定義嵌套的情況,#或者##僅在當前宏有效,嵌套宏中不會再次展開,既然當前宏無法展開,那么我只能再加一級宏定義作為轉換宏進行展開,看能不能解決該問題:
1#include此時輸出的結果符合我們的預期:2#include 3 4//#的簡單使用 5#defineSTR(str)#str 6 7//##的簡單使用 8#defineCMB(a,b)a##b 9 10#defineSTR_CON(str)STR(str)//轉換宏 11 12intmain(intargc,char*argv[]){ 13 14intCMB(uart,1)=5; 15 16printf("%s ",STR_CON(CMB(uart,1))); 17 18return0; 19}
首先進行第一層轉換宏替換處理掉##拼接符得到str(uart1),然后進行字符串轉換符的處理為uart1字符串打印輸出,當然以后你會遇到一些復雜的,不過要訣就是宏替換只會處理當前的#或者##,否則就需要增加轉換宏提前進行宏替換展開。 所以采用##拼接出來的標識符想要打印輸出的話,使用#進行轉換是最直接、方便的。
3##的玩法
##拼接符的玩法有點多,甚至有些還比較繞,當然如果你游刃有余的話,這對于重構代碼是一把“ 利器 ”。
1、在結構體定義中的妙用
下面是bug菌經常在項目代碼中用到的##結構體定義法,也是非常多開源代碼中慣用的做法,相比常規的結構體定義法,確實省去很多重復的代碼。 比如下面的參考代碼 :
1#include2、統一宏替換2#include 3 4#defineDF_STRUCT(name)typedefstructtag##namename; 5structtag##name 6 7DF_STRUCT(DevManage) 8{ 9intindex;//索引 10intAccess;//權限 11//... 12}; 13 14intmain(intargc,char*argv[]){ 15 16DevManagestDevManage; 17 18stDevManage.index=1; 19stDevManage.Access=666; 20 21printf("DevIndex:%d ",stDevManage.index); 22printf("DevAccess:%d ",stDevManage.Access); 23 24return1; 25}
拼接標識符意味著符號的粒度更高,而這碎片化的符號進行有效的管理,就可以使得符號更加具有通用性和靈活性。 其實這種思想跟我們代碼模塊話是同樣的道理。 來首先我們用一個兩層拼接體驗一下:
1#include2#include 3 4//假如這是stm32庫中的宏 5#defineGPIO_Pin_0((int)0x0001)/*! 編寫的思路bug菌在代碼中跟大家都標注了,相信大家一眼就能看懂,似乎并沒有想象中那么難。 而在前面介紹##的基礎知識提過,只要轉換宏寫得夠多,你可以一層套一層,最終獲得你想要的標識符,達到修改一個簡單的宏即可替換一整套宏的效果。 所以關鍵還是你要清晰的把拼接變量找出來,bug菌這里僅展示了一個拼接變量,當然多個也是同樣沒有問題的,跟我們函數傳遞參數一樣,不過這樣也會增加整個替換的復雜度,合理利用即可~ 審核編輯:湯梓紅
-
C語言
+關注
關注
180文章
7614瀏覽量
137732 -
字符串
+關注
關注
1文章
585瀏覽量
20604 -
C代碼
+關注
關注
1文章
89瀏覽量
14357 -
結構體
+關注
關注
1文章
130瀏覽量
10872
原文標題:同事C代碼中的#、##把我秀了~
文章出處:【微信號:TopSemic,微信公眾號:TopSemic嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論