有什麼作用呢?
主要是爲了方便管理軟件中的打印信息。我們在寫代碼或者修改bug時通常會將一些重要參數打印出來,方便我們debug,但是軟件發行的時候通常我們不希望有這些打印,可變參數宏就可以在這裏大顯身手,當然也可以用分級控制(ERR、INFO/ WARNING DEBUG)的方式來管理,實現起來也不麻煩,只要在這個宏中添加判斷語句就可以。
但是一般我們調試問題,不會把所有打印都打開,因爲工程具有一定規模的時候,打印非常多;通常我們只打開某個文件或者模塊的打印,這時可以再對應模塊中添加變了或者宏來控制打印。
常用的有2種方式:
#define DEBUG_PRINT(fmt,args...) do{printf(fmt"\r", ##args);}while(0)
與
#define DEBUG_PRINT(fmt, ...) do {printf(fmt"\r", ##__VA_ARGS__);}while(0)
效果一樣的, 下面就來分析一下這個原理:
1. 首先我們需要知道,可變參數宏是在C99標準中才實現的,以前沒定義這個 __VA_ARGS__宏,這個宏就代表可變參數列表,在GCC中 也支持args...這種寫法。
2. 關於do {...}while(0) 的用處多多,其中一個就是防止宏展開後,代碼出問題。
例如, if(true) PRINTF 如果這時printf中含有2兩以上代碼,那麼就會導致只能執行第一條。
3. ## 這個符號在宏定義中的作用是連接字符串,會忽略前後的空格, 如 A ## B 宏展開後爲 AB。 這裏加##字符串式爲了解決在__VA_ARGS__爲空時,後面多出來一個逗號的問題。個人猜測, ##連接符會將逗號與字符串鏈接,發現字符串爲空,因此就刪除其中的逗號,因爲__VA_ARGS__這個宏裏面,本身就是有可能包含任意多個逗號來支持多個參數。
4. fmt "\r" 這裏\r 是爲了兼容windows下換行符。
fmt 就是前面的第一個參數 ,是一個字符串裏面可以包含格式控制符, 這裏宏展開後 fmt 會直接展開成printf的第一個參數。然後在printf函數中這個字符串和“\r”會自動連接成一個字符串。(這個字符串連接功能的具體實現不知道是不是在printf函數中)
5. 關於宏展開我們可以再編譯時指定 -E參數 就可以查看到宏展開後的代碼。
如: gcc -E input.c -o ouput.i
查看ouput.i 就可以發現宏展開後的是什麼樣子
舉個例子:
/**********************************sum.c********************************************/
#include <stdio.h>
#ifdef DEBUG
#define DEBUG_PRINT(fmt,args...) do{printf(fmt"\r", ##args);}while(0)
#else
#define DEBUG_PRINT(fmt,args...) do{/*printf(fmt"\r", ##args)*/;}while(0)
#endif
int sum(int a, int b);
int main()
{
int result = 0;
result = sum(1, 2);
printf("result = %d\n", result);
return 0;
}
int sum(int a, int b)
{
DEBUG_PRINT("a = %d, b = %d\n", a, b);
return a+b;
}
/**********************************end********************************************/
當這個文件有幾百行幾千行的時候裏面可以有很多打印信息,我們可以通過DEBUG宏來控制打印信息是否顯示出來。
這樣當關閉DEBUG宏時也不會有警告信息。