宏函數(shù)在項(xiàng)目開(kāi)發(fā)中用的頻率非常高,跟普通函數(shù)相比,它沒(méi)有復(fù)雜的調(diào)用步驟,也不需要給形參分配空間,所以很多場(chǎng)景都需要宏函數(shù)的存在。
?
簡(jiǎn)單的宏函數(shù)確實(shí)也挺簡(jiǎn)單,比如這樣的無(wú)參宏函數(shù),在代碼中凡是出現(xiàn)debug的地方,都會(huì)替換成printf這條輸出語(yǔ)句。
#define debug printf("helloworld ") void main() { ????debug;??//?等價(jià)于?printf("helloworld"); }? 復(fù)雜一點(diǎn)的,在宏函數(shù)中加個(gè)參數(shù),我們把它稱(chēng)作有參宏函數(shù),比如這樣的:
#define?debug(s)?printf("%s ", s) void main() { ????debug("helloworld "); }調(diào)用debug的時(shí)候,需要傳個(gè)參數(shù)進(jìn)去,當(dāng)然這個(gè)參數(shù)必須是字符串,如果隨便寫(xiě)個(gè)數(shù)字,運(yùn)行的時(shí)候就是段錯(cuò)誤。
#define debug(s) printf("%s ", s) void main() { ????debug(1);????//段錯(cuò)誤 }? 這也把宏函數(shù)的缺點(diǎn)暴露了出來(lái),參數(shù)沒(méi)有類(lèi)型限制,不夠安全。 再回到文章剛開(kāi)始的地方。
?
這個(gè)宏函數(shù)不僅有參,而且還是可變參數(shù),在代碼中凡是出現(xiàn)debug的地方,都把他替換成fprintf。 唯一不太好懂的地方,可能是args前面出現(xiàn)了兩個(gè)井號(hào)。 兩個(gè)井號(hào)在C語(yǔ)言中被稱(chēng)為連接符號(hào),功能就是在帶參的宏函數(shù)中將兩個(gè)字串連接成一個(gè)新的字符串。 舉個(gè)例子,有這樣一個(gè)宏函數(shù):
#define name(x) name_##x void main() { int name_1, name_2; ????name(1);????????//?等價(jià)于?name_1; }? 如果調(diào)用的時(shí)候參數(shù)傳入1,就被替換成了name_1。 在可變參數(shù)中,兩個(gè)井號(hào)就是把所有參數(shù)連接在后面。 宏函數(shù)的使用場(chǎng)景很多,就拿圖上這個(gè)來(lái)說(shuō),可以實(shí)現(xiàn)項(xiàng)目開(kāi)發(fā)的時(shí)候打開(kāi)調(diào)試信息,方便調(diào)試代碼。項(xiàng)目完成后關(guān)閉調(diào)試信息。我們來(lái)個(gè)測(cè)試代碼。
#include在主函數(shù)中調(diào)用debug函數(shù),如果你希望debug函數(shù)執(zhí)行,編譯的時(shí)候提供DEBUG宏定義就行。#ifdef DEBUG #define debug(format, args...) fprintf(stderr, format, ##args) #else #define debug(format, args...) #endif int main() { int a = 1; debug("a = %d ", a); return 0; }
gcc test.c -o test -DDEBUG? 如果你不希望信息輸出,編譯的時(shí)候就不要管它。
gcc test.c -o test? 這個(gè)方法比項(xiàng)目完成后,一行一行去刪除調(diào)試信息來(lái)的更方便。 如果你看過(guò)一些開(kāi)源代碼,肯定會(huì)發(fā)現(xiàn)很多宏定義中使用do while語(yǔ)句。
#define NS_GET16(s, cp) do { const unsigned char *t_cp = (const unsigned char *)(cp); (s) = ((uint16_t)t_cp[0] << 8) | ((uint16_t)t_cp[1]) ; (cp) += NS_INT16SZ; } while (0) #define NS_GET32(l, cp) do { const unsigned char *t_cp = (const unsigned char *)(cp); (l) = ((uint32_t)t_cp[0] << 24) | ((uint32_t)t_cp[1] << 16) | ((uint32_t)t_cp[2] << 8) | ((uint32_t)t_cp[3]) ; (cp) += NS_INT32SZ; } while (0)? 雖然看不懂,但還是覺(jué)得這段代碼寫(xiě)的非常厲害,那你知道為什么要這樣寫(xiě)嗎? 1.可以避免空的宏定義出現(xiàn)warning。
#define foo()?
?
有些編譯器對(duì)這樣的代碼會(huì)提示警告,do while可以消除警告。
?
#define foo() do {}while(0)
?
2.作為一個(gè)獨(dú)立的單元,可以定義變量或者是進(jìn)行更復(fù)雜的運(yùn)算。
?
#define debug do { int a; printf("helloworld"); foo(); }while (0)
?
3.放在判斷語(yǔ)句中,避免語(yǔ)法錯(cuò)誤。
?
#include#define debug(num) num--; printf("%d ", num) int main() { int num = 1; if (num > 0) debug(num); else printf("error "); return 0; }
?
這個(gè)代碼編譯的時(shí)候會(huì)提示語(yǔ)法錯(cuò)誤,因?yàn)楹甓x中包含了兩條語(yǔ)句,同時(shí)判斷語(yǔ)句中又沒(méi)有使用大括號(hào)。do while可以解決這個(gè)問(wèn)題。
?
#include#define debug(num) do {num--; printf("%d ", num);} while(0) int main() { int num = 1; if (num > 0) debug(num); else printf("error "); return 0; }
?
總結(jié)一下,do while可以把復(fù)雜的語(yǔ)句包裹起來(lái),使它成為一個(gè)單獨(dú)的單元,避免語(yǔ)法問(wèn)題。而且大部分的編譯器都能識(shí)別while(0)這種無(wú)效的循環(huán),并且把它優(yōu)化掉,不會(huì)造成效率上的問(wèn)題。
審核編輯:湯梓紅
?
評(píng)論
查看更多