stdarg.h是C语言中C标准函数库的头文件,stdarg是由standard(标准) arguments(参数)简化而来,主要目的为让函数能够接收可变参数。C++的cstdarg头文件中也提供这样的功能;虽然与C的头文件是兼容的,但是也有冲突存在。可变参数函数(Variadic functions)是stdarg.h内容典型的应用,虽然也可以使用在其他由可变参数函数调用的函数(例如vprintf)。(参加http://baike.baidu.com/link?url=DA_U06PN2MwV09FoE2YXODNhl0P4xmHXgjM-vNzcnkMGFyLdvvP1KLkyZ8GYpiZLNUIh_WG5cqvPHNJCM3mnOKX_cieBFZCV4-7cFd1IkMW)
stdarg.h(vs2013的win32平台)的内容:
#pragmaonce
#ifndef _INC_STDARG
#define_INC_STDARG
#include<crtdefs.h>
#if !defined (_WIN32)
#error ERROR: Only Win32 targetsupported!
#endif /* !defined (_WIN32) */
#include<vadefs.h>
#ifdef__cplusplus
extern"C" {
#endif /* __cplusplus */
#defineva_start_crt_va_start
#defineva_arg_crt_va_arg
#defineva_end_crt_va_end
void_CRTIMP__cdecl _vacopy(_Out_va_list *,_In_va_list);
#defineva_copy(apd, aps) _vacopy(&(apd), aps)
#ifdef__cplusplus
}
#endif/* __cplusplus */
#endif /* _INC_STDARG
它定义了四个宏:va_start,va_arg, va_end, va_copy
其中,va_start, va_arg,va_end宏定义如下:
#define_crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define_crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define_crt_va_end(ap) ( ap = (va_list)0 )
其中:#define_INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1)& ~(sizeof(int) - 1) )
使用这三个宏时均会用到一个参数ap,它的类型为va_list, va_list的定义如下:
#ifdef_M_CEE_PURE
typedefSystem::ArgIterator va_list;
#else /* _M_CEE_PURE */
typedefchar * va_list;
#endif /* _M_CEE_PURE */
在大多系统中,va_list相当于char*,用于存储可变参数的地址。
下面介绍这三个宏的作用:
(1)宏va_start(ap,v)的作用是得到第一个可变参数的地址,_ADDRESSOF(v) + _INTSIZEOF(v),这里_ADDRESSOF(v)为最后一个固定参数的地址,_INTSIZEOF(v)为这个固定参数实际占用字节数,故相加后就得到了第一个可变参数的起始地址了,va_start(ap,v)用来将ap指向第一个可变参数的地址。
(2)宏va_arg(ap,t)的作用是把ap指向当前可变参数的下一个可变参数的地址,并返回当前可变参数的内容,我们来看它的实现。
( *(t *)((ap +=_INTSIZEOF(t)) - _INTSIZEOF(t)) )
首先执行最里面的括号的内容:(ap +=_INTSIZEOF(t)),该表达式等价于ap = ap+ _INTSIZEOF(t),将ap指向当前参数的下一个参数,那么表达式变为( *(t *)(ap - _INTSIZEOF(t)) ),此时ap已经指向当前可变参数的下一个可变参数,ap - _INTSIZEOF(t)得到的指针,指向了当前可变参数,但此处ap没有赋值操作,因此,ap不变,仍指向当前可变参数的下一个可变参数。( *(t *)(ap - _INTSIZEOF(t)) )将返回当前可变参数的值;
(3)宏va_end(ap)的作用是将ap指针关掉,它的实现为( ap = (va_list)0 ),使得ap不再指向可变参数的地址,相当于给它置为NULL,以免出现危险,在自己定义的可变函数的末尾用到,用于释放va_start以及va_arg占用的内存。
下面是一个简单的int类型变量相加的例子:
#include<stdio.h>
#include<string.h>
#include<stdarg.h>
void simple_add_fun(intstart, ...)
{
va_list arg_ptr;
int sum =start;
va_start(arg_ptr,start);//使arg_ptr指向第一个可变参数
do {
sum += va_arg(arg_ptr,int); //得到可变参数的值并与sum相加,同时
// arg_ptr下一个可变参数
if (-1 == *arg_ptr) // -1作为结束标志
{
break;
}
} while (true);
printf("sum: %d\n", sum);
va_end(arg_ptr);
return;
}
int main(intargc,char*argv[])
{
simple_add_fun(100, 200, 3013, -1);
return 0;
}
最后介绍宏va_copy。
va_copy(apd, aps)使用较少,用于将va_list类型变量aps赋值给apd。此时,我们可能会有疑问了,直接通过va_list apd = aps这种方式赋值不就行了吗?干嘛需要定义一个va_copy的宏呢?
通过查阅资料发现(参考https://linux.die.net/man/3/va_copy)在有些系统中,va_list类型为一个长度为1的数组,此时需要采用*apd=*aps的赋值方式,故封装一个va_copy宏来自适应地完成赋值工作。相应地,每一个va_copy(apd,aps)需要一个va_end(apd)来配对使用,将apd置为NULL,以免出现危险。
下面是网站c++学习网站(http://www.cplusplus.com)上给出的一个例子,用于打印输入的int值:
/* va_copy example */
#include<stdio.h> /* printf, vprintf*/
#include<stdlib.h> /* malloc */
#include<string.h> /* strlen, strcat */
#include<stdarg.h> /* va_list, va_start, va_copy, va_arg, va_end */
/* print ints until a zero is found: */
void PrintInts(intfirst, ...)
{
char * buffer;
constchar * format ="[%d] ";
int count = 0;
int val =first;
va_list vl, vl_count;
va_start(vl,first);
/* count number of arguments: */
va_copy(vl_count, vl);
while (val != 0) {
val = va_arg(vl_count,int);
++count;
}
va_end(vl_count);
/* allocate storage for formatstring: */
buffer = (char*)malloc(strlen(format)*count + 1);
buffer[0] = '\0';
/* generate format string: */
for (; count>0; --count) {
strcat(buffer, format);
}
/* print integers: */
printf(format, first);
vprintf(buffer, vl);
va_end(vl);
}
int main()
{
PrintInts(10, 20, 30, 40, 50, 0);
return 0;
}
结果为
[10] [20] [30] [40] [50] [0]