總述
? 宏的使用耗啦,大家經(jīng)常會用,但是一般只是簡單定義一個符號常量机杜,類似于#define WHEEL_SCALE_MM 0.53f帜讲、
#define LOG_I(tag, text_fmt, ...) log_i(tag, text_fmt, ##VA_ARGS) ,但是除此之外還有宏還有個##粘貼作用椒拗,可以配合#define這個常量表達式似将,可以做成一個宏定義指針函數(shù)列表,繼而查詢執(zhí)行函數(shù)蚀苛。
? 話不多說在验,我們開始今天的分享,首先還是會進行一下常規(guī)的描述堵未,再分享"##"粘貼的妙用腋舌。
作者:良知猶存
轉(zhuǎn)載授權(quán)以及圍觀:歡迎添加微信公眾號:羽林君
*一、#define的常規(guī)操作*
? #define預(yù)處理器指令和其他預(yù)處理器指令一樣渗蟹, 以#號作為一行的開始块饺。ANSI和后來的標(biāo)準(zhǔn)都允許#號前面有空格或制表符, 而且還允許在#和指令的其余部分之間有空格雌芽。但是舊版本的C要求指令從一行最左邊開始授艰,而且#和指令其余部分之間不能有空格。指令可以出現(xiàn)在源文件的任何地方世落, 其定義從指令出現(xiàn)的地方到該文件末尾有效淮腾。我們大量使用#define指令來定義明示常量(manifest constant) (也叫做符號常量) 。
? 預(yù)處理器指令從#開始運行屉佳, 到后面的第1個換行符為止谷朝。也就是說缤底, 指令的長度僅限于一行哺哼。然而离唬, 在預(yù)處理開始前椎麦, 編譯器會把多行物理行處理為一行邏輯行。
一般我們會用#define 來進行明示常量榔组,或者做一個簡單的宏替換函數(shù)
#define RX_BUF_SIZE 30#define MBEDTLS_DES_C ? ? ?? /*數(shù)據(jù)加密*/
#define ExitIsr ? Encoder_Isr ? void Encoder_Isr(void){
? g.dir_count += (g.dir == 1)? 1 : -1;
}
? 每行#define(邏輯行) 都由3部分組成。第1部分是#define指令本身。第2部分是選定的縮寫干旁, 也稱為宏。有些宏代表值(如本例) 炮沐, 這些宏被稱為類對象宏 争群。C 語言還有類函數(shù)宏 , 稍后討論大年。宏的名稱中不允許有空格换薄, 而且必須遵循C變量的命名規(guī)則:只能使用字符玉雾、 數(shù)字和下劃線(_) 字符, 而且首字符不能是數(shù)字轻要。第3部分(指令行的其余部分) 稱為替換列表或替換體 复旬。?
? 一旦預(yù)處理器在程序中找到宏的實例后, 就會用替換體代替該宏冲泥。從宏變成最終替換文本的過程稱為宏展開驹碍。注意, 可以在#define行使用標(biāo)準(zhǔn)C注釋凡恍。如前所述志秃, 每條注釋都會被一個空格代替。
此外我們還會比較多的使用變宏參
通過把宏參數(shù)列表中最后的參數(shù)寫成省略號(即嚼酝, 3個點...) 來實現(xiàn)這一功能浮还。這樣, 預(yù)定義宏_ VA_ARGS _可用在替換部分中闽巩, 表明省略號代表什么钧舌。
#define PR(...) printf(_ _VA_ARGS_ _)
假設(shè)稍后調(diào)用該宏:
PR("Howdy");PR("weight = %d, shipping = $%.2f\n", wt, sp);
對于第1次調(diào)用, _ VA_ARGS 展開為1個參數(shù):"Howdy"又官。對于第2次調(diào)用延刘, _ _VA_ARGS _展開為3個參數(shù):"weight = %d,shipping = $%.2f\n"、 wt六敬、 sp碘赖。
因此, 展開后的代碼是:
printf("Howdy");printf("weight = %d, shipping = $%.2f\n", wt, sp);
*二外构、#define配合##使用*
? 很多人應(yīng)該都知道"##"的用法普泡,它被稱為預(yù)處理的粘合劑,與#運算符類似审编,##運算符可用于類函數(shù)宏的替換部分撼班。而且,##還可以用于對象宏的替換部分垒酬。##運算符可以把兩個記號組合成一個記號砰嘁。
#define def_u32_array(__name, __size) ? ? uint32_t array_##__name[__size];
實際中,我們可以這樣使用:
def_u32_array(sample_buffer, 64)
宏展開的效果是:
uint32_t array_sample_buffer[64];
同樣類比于初始化一個數(shù)組勘究,我們也可以粘貼形成一個函數(shù)
下面就是在Linux內(nèi)核里面的源代碼:
其中這個__pcpu_size_call_return宏矮湘,通過##粘貼選擇要使用的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ù)再次定義為一個宏函數(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); ? ? ? ? ? ? ? ? ? ?? \
})
最后面進行執(zhí)行 __this_cpu_read(current_kprobe);
int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)
{
? struct kprobe *p
? p = __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++中我們也可以將做成一個指針列表缅阳,對應(yīng)好每個函數(shù)的名稱后,再次調(diào)用該定義的宏參數(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)核中的代碼秀撇,用來print不同狀態(tài)的打印信息,如果大家想要快速掌握這些使用方法向族,建議大家擼一擼Linux內(nèi)核源碼呢呵燕。
這就是我分享的#define的操作方法,里面代碼是實踐過的炸枣,如果大家有什么更好的思路虏等,歡迎分享交流哈。
*—**END*—
推薦閱讀
【1】c++nullptr(空指針常量)适肠、constexpr(常量表達式)
【2】嵌入式底層開發(fā)的軟件框架簡述 必讀【3】CPU中的程序是怎么運行起來的【4】C++的匿名函數(shù)(lambda表達式)【5】階段性文章總結(jié)分析
本公眾號全部原創(chuàng)干貨已整理成一個目錄霍衫,后臺回復(fù)【資源】即可獲得。