谈到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__ < 218 19 #ifndef __sparc__20 #define va_start(AP, LASTARG) \21 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))22 #else23 #define va_start(AP, LASTARG) \24 (__builtin_saveregs (), \25 AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))26 #endif27 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 #else41 #define va_start(AP, LASTARG) \42 (__builtin_saveregs (), AP = ((char *) __builtin_next_arg ()))43 #endif44 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行:定义了用于编译器的内存对齐宏(参见):
#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 )