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