總述
宏的使用,大家經(jīng)常會(huì)用,但是一般只是簡(jiǎn)單定義一個(gè)符號(hào)常量,類(lèi)似于
#define WHEEL_SCALE_MM?0.53f、
#define LOG_I(tag, text_fmt, ...) log_i(tag, text_fmt, ##__VA_ARGS__)?
但是除此之外還有宏還有個(gè)##粘貼作用,可以配合#define這個(gè)常量表達(dá)式,可以做成一個(gè)宏定義指針函數(shù)列表,繼而查詢(xún)執(zhí)行函數(shù)。
話不多說(shuō),我們開(kāi)始今天的分享,首先還是會(huì)進(jìn)行一下常規(guī)的描述,再分享"##"粘貼的妙用。
作者:良知猶存
轉(zhuǎn)載授權(quán)以及圍觀:歡迎添加微信公眾號(hào):羽林君
一、#define的常規(guī)操作
#define預(yù)處理器指令和其他預(yù)處理器指令一樣, 以#號(hào)作為一行的開(kāi)始。ANSI和后來(lái)的標(biāo)準(zhǔn)都允許#號(hào)前面有空格或制表符, 而且還允許在#和指令的其余部分之間有空格。
但是舊版本的C要求指令從一行最左邊開(kāi)始,而且#和指令其余部分之間不能有空格。指令可以出現(xiàn)在源文件的任何地方, 其定義從指令出現(xiàn)的地方到該文件末尾有效。我們大量使用#define指令來(lái)定義明示常量(manifest constant) (也叫做符號(hào)常量) 。
預(yù)處理器指令從#開(kāi)始運(yùn)行, 到后面的第1個(gè)換行符為止。也就是說(shuō), 指令的長(zhǎng)度僅限于一行。然而,?在預(yù)處理開(kāi)始前, 編譯器會(huì)把多行物理行處理為一行邏輯行。
一般我們會(huì)用#define 來(lái)進(jìn)行明示常量,或者做一個(gè)簡(jiǎn)單的宏替換函數(shù)
#define RX_BUF_SIZE 30#define MBEDTLS_DES_C /*數(shù)據(jù)加密*/#define ExitIsr Encoder_Isrvoid Encoder_Isr(void){g.dir_count += (g.dir == 1)? 1 : -1;}
每行#define(邏輯行) 都由3部分組成。第1部分是#define指令本身。第2部分是選定的縮寫(xiě), 也稱(chēng)為宏。有些宏代表值(如本例) , 這些宏被稱(chēng)為類(lèi)對(duì)象宏?。
C 語(yǔ)言還有類(lèi)函數(shù)宏?, 稍后討論。宏的名稱(chēng)中不允許有空格, 而且必須遵循C變量的命名規(guī)則:只能使用字符、 數(shù)字和下劃線(_) 字符, 而且首字符不能是數(shù)字。第3部分(指令行的其余部分) 稱(chēng)為替換列表或替換體?。
????
一旦預(yù)處理器在程序中找到宏的實(shí)例后, 就會(huì)用替換體代替該宏。從宏變成最終替換文本的過(guò)程稱(chēng)為宏展開(kāi)。注意, 可以在#define行使用標(biāo)準(zhǔn)C注釋。如前所述, 每條注釋都會(huì)被一個(gè)空格代替。
此外我們還會(huì)比較多的使用變宏參
通過(guò)把宏參數(shù)列表中最后的參數(shù)寫(xiě)成省略號(hào)(即, 3個(gè)點(diǎn)...) 來(lái)實(shí)現(xiàn)這一功能。這樣, 預(yù)定義宏_ _VA_ARGS_ _可用在替換部分中, 表明省略號(hào)代表什么。?
#define?PR(...)?printf(_?_VA_ARGS_?_)
假設(shè)稍后調(diào)用該宏:
PR("Howdy");PR("weight = %d, shipping = $%.2f\n", wt, sp);
對(duì)于第1次調(diào)用, _ _VA_ARGS_ _展開(kāi)為1個(gè)參數(shù):"Howdy"。
對(duì)于第2次調(diào)用, _ _VA_ARGS_ _展開(kāi)為3個(gè)參數(shù):"weight = %d,shipping = $%.2f\n"、 wt、 sp。
因此, 展開(kāi)后的代碼是:
printf("Howdy");printf("weight = %d, shipping = $%.2f\n", wt, sp);
二、#define配合##使用
很多人應(yīng)該都知道"##"的用法,它被稱(chēng)為預(yù)處理的粘合劑,與#運(yùn)算符類(lèi)似,##運(yùn)算符可用于類(lèi)函數(shù)宏的替換部分。而且,##還可以用于對(duì)象宏的替換部分。##運(yùn)算符可以把兩個(gè)記號(hào)組合成一個(gè)記號(hào)。
#define?def_u32_array(__name,?__size)?????uint32_t?array_##__name[__size];
實(shí)際中,我們可以這樣使用:
def_u32_array(sample_buffer, 64)
宏展開(kāi)的效果是:
uint32_t?array_sample_buffer[64];
同樣類(lèi)比于初始化一個(gè)數(shù)組,我們也可以粘貼形成一個(gè)函數(shù)。
下面就是在Linux內(nèi)核里面的源代碼:
其中這個(gè)__pcpu_size_call_return宏,通過(guò)##粘貼選擇要使用的raw_cpu_read_x 函數(shù)。
#define __pcpu_size_call_return(stem, variable) \({ \typeof(variable) pscr_ret__; \__verify_pcpu_ptr(&(variable)); \switch(sizeof(variable)) { \case 1: pscr_ret__ = stem##1(variable); break; \case 2: pscr_ret__ = stem##2(variable); break; \case 4: pscr_ret__ = stem##4(variable); break; \case 8: pscr_ret__ = stem##8(variable); break; \default: \__bad_size_call_parameter(); break; \} \pscr_ret__; \})#define raw_cpu_read_1(pcp) raw_cpu_generic_read(pcp)#define raw_cpu_generic_read(pcp) \({ \*raw_cpu_ptr(&(pcp)); \})
這部分是更高層次的宏定義,將##粘貼的函數(shù)再次定義為一個(gè)宏函數(shù)
#define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, pcp)#define __this_cpu_read(pcp) \({ \__this_cpu_preempt_check("read"); \raw_cpu_read(pcp); \})
最后面進(jìn)行執(zhí)行?__this_cpu_read(current_kprobe);
int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs){struct kprobe *pp = __this_cpu_read(current_kprobe);??p?=?get_kprobe((unsigned?long?*)addr);????... 省略多行代碼??if (p->break_handler && p->break_handler(p, regs)) {setup_singlestep(p, regs);kcb->kprobe_status = KPROBE_HIT_SS;return 1;}
在C++中我們也可以將做成一個(gè)指針列表,對(duì)應(yīng)好每個(gè)函數(shù)的名稱(chēng)后,再次調(diào)用該定義的宏參數(shù),就實(shí)現(xiàn)了指針調(diào)用。
#define AddFunc(Func) \FuncPtrTemplate Func##Map(int mode_name, int state_name) \{ \static auto modeMap = Func##Register(); \auto pair = std::make_pair(mode_name, state_name); \auto mapEntry = modeMap->find(pair); \if (mapEntry == modeMap->end()) \return nullptr; \return mapEntry->second; \} \bool Mode::Func(State *state) { \auto state_id = getStateId(); \auto p_function = Func##Map(getId(), state_id); \if (p_function) \return p_function(this, state); \return false; \}AddFunc(IsExit);int?main(){IsExit(p.get());}
這也是Linux內(nèi)核中的代碼,用來(lái)print不同狀態(tài)的打印信息,如果大家想要快速掌握這些使用方法,建議大家擼一擼Linux內(nèi)核源碼呢。
?這就是我分享的#define的操作方法,里面代碼是實(shí)踐過(guò)的,如果大家有什么更好的思路,歡迎分享交流哈。
—END—
猜你喜歡
什么是不完全類(lèi)型?
C語(yǔ)言、嵌入式中幾個(gè)非常實(shí)用的宏技巧
1024G 嵌入式資源大放送!包括但不限于C/C++、單片機(jī)、Linux等。在公眾號(hào)聊天界面回復(fù)1024,即可免費(fèi)獲取!
免責(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)系我們,謝謝!





