談到C語言中可變參數函數的實現(參見C語言中可變參數函數實現原理),有一個頭文件不得不談,那就是stdarg.h
本文從minix源碼中的stdarg.h頭文件入手進行分析:

1 #ifndef _STDARG_H 2 #define _STDARG_H 3 4 5 #ifdef __GNUC__ 6 /* The GNU C-compiler uses its own, but similar varargs mechanism. */ 7 8 typedef char *va_list; 9 10 /* Amount of space required in an argument list for an arg of type TYPE. 11 * TYPE may alternatively be an expression whose type is used. 12 */ 13 14 #define __va_rounded_size(TYPE) \ 15 (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) 16 17 #if __GNUC__ < 2 18 19 #ifndef __sparc__ 20 #define va_start(AP, LASTARG) \ 21 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) 22 #else 23 #define va_start(AP, LASTARG) \ 24 (__builtin_saveregs (), \ 25 AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) 26 #endif 27 28 void va_end (va_list); /* Defined in gnulib */ 29 #define va_end(AP) 30 31 #define va_arg(AP, TYPE) \ 32 (AP += __va_rounded_size (TYPE), \ 33 *((TYPE *) (AP - __va_rounded_size (TYPE)))) 34 35 #else /* __GNUC__ >= 2 */ 36 37 #ifndef __sparc__ 38 #define va_start(AP, LASTARG) \ 39 (AP = ((char *) __builtin_next_arg ())) 40 #else 41 #define va_start(AP, LASTARG) \ 42 (__builtin_saveregs (), AP = ((char *) __builtin_next_arg ())) 43 #endif 44 45 void va_end (va_list); /* Defined in libgcc.a */ 46 #define va_end(AP) 47 48 #define va_arg(AP, TYPE) \ 49 (AP = ((char *) (AP)) += __va_rounded_size (TYPE), \ 50 *((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE)))) 51 52 #endif /* __GNUC__ >= 2 */ 53 54 #else /* not __GNUC__ */ 55 56 57 typedef char *va_list; 58 59 #define __vasz(x) ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int) -1)) 60 61 #define va_start(ap, parmN) ((ap) = (va_list)&parmN + __vasz(parmN)) 62 #define va_arg(ap, type) \ 63 (*((type *)((va_list)((ap) = (void *)((va_list)(ap) + __vasz(type))) \ 64 - __vasz(type)))) 65 #define va_end(ap) 66 67 68 #endif /* __GNUC__ */ 69 70 #endif /* _STDARG_H */
從代碼中可以看到,里面編譯器的版本以及相關的大量宏定義
第5行: #ifdef __GNUC__
作用是條件編譯,__GNUC__為GCC中定義的宏。GCC的版本,為一個整型值。如果你需要知道自己的程序是否被GCC編譯,可以簡單的測試一下__GNUC__,假如你代碼需要運行在GCC某個特定的版本下,那么你就要小心了,因為GCC的主要版本在增加,如果你想定義宏的方式直接實現控制,你可以寫如下的代碼(參見伯克利大學網站):
/* 測試 GCC > 3.2.0 ? */ #if __GNUC__ > 3 || \ (__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || \ (__GNUC_MINOR__ == 2 && \ __GNUC_PATCHLEVEL__ > 0))
你還可以使用下面一個類似的方法:
#define GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) ... /*測試 GCC > 3.2.0 ?*/ #if GCC_VERSION > 30200
第8行: 使用typedef進行了一個聲明:typedef char *va_list;
第14行:定義了用於編譯器的內存對齊宏(參見C語言內存對齊詳解(3)):
#define __va_rounded_size(TYPE) \ (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
第17行:#if __GNUC__ < 2,進行GCC的版本判斷,看當前版本是否大於2
第19行:#ifndef __sparc__ 可擴充處理器架構宏(以后再深入研究)
第20行:使得ap指向函數中的第一個無名參數的首地址的宏:
#define va_start(AP, LASTARG) \ (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
第31行:
#define va_arg(AP, TYPE) \ (AP += __va_rounded_size (TYPE), \ *((TYPE *) (AP - __va_rounded_size (TYPE))))
va_arg宏使得ap指向下一個參數,已經處理了內存對齊,其中參數的類型為TYPE
第48行:
void va_end (va_list); /* Defined in gnulib */
定義在gnulib中,va_end 與va_start成對使用.在有些代碼中定義為:
#define va_end(ap) ( ap = (va_list)0 )