GCC 支持復雜的宏,它使用一種不同的語法,使你可以給可變參數一個名字,如同其它參數一樣,比如:
引用#define debug(format, args...) fprintf(stderr, format, args)
這種定義可讀性更強,也更容易描述。完整測試代碼:
引用#include <stdio.h>
#define debug(format, args...) fprintf(stderr, format, args)
int main()
{
char a[20] = "hello world \n";
int i = 10;
debug("i = %d, %s", i, a);
return 0;
}
運行輸出:
引用beyes@linux-beyes:~/C/micro> ./mic.exe
i = 10, hello world
但是上面的定義仍存在一點問題,如果把上面的代碼改為下面的:
引用#include <stdio.h>
#define debug(format, args...) fprintf(stderr, format, args)
int main()
{
debug("hello world \n");
return 0;
}
那么在編譯時會提示以下錯誤:
引用beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
mic.c: In function ‘main’:
mic.c:10: error: expected expression before ‘)’ token
提示缺少右邊括號。這是因為,當宏展開后,"hello world\n" 代入 format,然而,在其后還緊跟着一個逗號,但是這個逗號后面是期望有 args 參數的,但這里卻沒有,所以宏不能展開完全,故而無法編譯通過。那么,再改一下宏定義:
引用#include <stdio.h>
#define debug(format, args...) fprintf(stderr, format, ##args)
int main()
{
debug("hello world \n");
return 0;
}
這時候,再編譯運行及輸出:
引用beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
beyes@linux-beyes:~/C/micro> ./mic.exe
hello world
編譯通過,並正常輸出。上面的代碼,在 fprintf() 中的 args 前面加了兩個 # 號 ##。
## 號的作用是:
如果可變參數部分( args...) 被忽略或為空,那么 "##" 操作會使預處理器 (preprocessor) 去掉它前面的那個逗號。如果在調用宏時,確實提供了一些可變參數,GNU C 也會正常工作,它會把這些可變參數放在逗號的后面;如果沒有提供,它就會自動去掉前面的逗號,使宏結束展開 ---- 補充完右邊括號。
另外,假如按照 C99 的定義來用,改宏為:
引用#define debug(format, args...) fprintf(stderr, format, ##__VA_ARGS__)
那么編譯會出錯:
引用beyes@linux-beyes:~/C/micro> gcc -g mic.c -o mic.exe
mic.c:3:58: warning: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro
mic.c:9:1: error: pasting "," and "__VA_ARGS__" does not give a valid preprocessing token
mic.c: In function ‘main’:
mic.c:9: error: ‘__VA_ARGS__’ undeclared (first use in this function)
mic.c:9: error: (Each undeclared identifier is reported only once
mic.c:9: error: for each function it appears in.)
原因在於,args... 和 ##__VA_ARGS__ 是不匹配的,正確的匹配應該是:
引用#define debug(format, ...) fprintf(stderr, format, ##__VA_ARGS__)
注意,... 省略號對應的就是 __VA_ARGS__
一般的,定義可變參數宏的一個流行方法,形如:
引用#define DEBUG(args) (printf("DEBUG: "), printf args)
if(n != 0) DEBUG(("n is %d \n", n));
這個方法的一個缺點是,要記住一對額外的括弧。