定義宏時可以讓宏接收可變參數,對於可變參數的定義,標准 C 和 GNU C(GNU 對 C的擴展)是不一樣的。
標准 C
標准 C 對於可變參數的定義如下,使用...:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
在宏定義中,__VA_ARGS__ 代表了所有的可變參數。比如像下面使用宏 eprintf:
eprintf ("%s:%d: ", input_file, lineno)
那么 __VA_ARGS__ 就是 "%s:%d: ", input_file, lineno。
GNU C
GNU C 除了支持標准 C的定義之外,還有自己的一套定義,同樣是定義宏 eprintf,GNU C 中可以定義成:
#define eprintf(args...) fprintf (stderr, args)
和標准 C 不同之處在於可變參標志...前面有一個參數名 args,在宏定義里面也沒有 __VA_ARGS__,而是直接使用的參數名 args,兩者是等價的,但是如果使用 GNU C 的方式,在宏定義中就不可以使用 __VA_ARGS__。
可變參數為空
假如定義了如下宏(不管是使用標准 C 方式還是 GNU C方式都可以,這里使用標准 C 的方式):
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
在標准 C 的環境下進行如下調用,不傳遞可變參數:
eprintf("success!\n");
宏的擴展將會報錯,因為擴展出來后的形式如下:
fprintf(stderr, "success!\n",);
fprintf 最后一個參數后面多了一個逗號,這樣將報錯。這種情況在標准 C 下無法解決,但是 GNU C可以解決。GNU C 賦予 "##" 另一種特殊意義(不是字符串連接的意義了),如果在可變參數前面加上"##",當可變參數為空時,前面的逗號會被刪除:
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__) // 或者 #define eprintf(format, args...) fprintf(stderr, format, ##args)
當可變參數為空時,宏都會擴展成:
fprintf(stderr, "success!\n");
可以看到最后一個參數后面的逗號被刪除了。
Clang 默認使用 GNU11,因此也支持這個功能。