例如:
void ConnectData(int i,...)
在上面的代码中,编译器只检查第一个参数是否为整型,而不对其他参数进行检查。
对于可变参数的函数,需要进行特殊的处理。首先需要引用 <stdarg.h> 头文件,然后利用va_list类型和va_start、va_arg、va_end 3个宏读取传递到函数中的参数值。
这几个宏的定义如下(在 ANSI C 中):
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
void va_start( va_list arg_ptr, prev_param );
说明如下:
va_start
sets arg_ptr to the first optional argument in the list of arguments passed to the function. The argument arg_ptr must have va_list type. The argument prev_param is the name of the required parameter immediately preceding the first optional argument in the argument list. If prev_param is declared with the register storage class, the macro’s behavior is undefined. va_start must be used before va_arg is used for the first time.
【 va_start函数将参数arg_ptr设置为可变参数列表的第一个参数。参数arg_ptr的类型必须为va_list。参数prev_param是在可变参数列表之前的那一个参数。( 也就是说在 ANSI C 中,如果一个函数有可变参数,那么在该可变参数前必须有一个明确定义的参数,否则无法调用函数 va_start ,例如函数 int add(int i,...)是合法的,而函数 int add(...)是不合法的。 )】
va_arg
retrieves a value of type from the location given by arg_ptr and increments arg_ptr to point to the next argument in the list, using the size of type to determine where the next argument starts. va_arg can be used any number of times within the function to retrieve arguments from the list.
【 va_arg函数将返回 arg_ptr 所指位置的值,并将 arg_ptr 指向下一个参数 】
va_end
After all arguments have been retrieved, va_end resets the pointer to NULL.
示例代码:
#include<stdarg.h>
#include<iostream>
using namespace std;
int add(int pre,...)
{
va_list arg_ptr;//需要定义一个这个类型,va_list。来遍历形参数列表
int sum=0;
int nArgValue;
sum+=pre;
va_start(arg_ptr,pre);//arg_ptr必须为va_list类型。pre为add可变参数的第一个形参,所以可变形参列表中,必须要有第一个形参。
do{
nArgValue=va_arg(arg_ptr,int);//作用是 得到arg_ptr所指元素的值,如果然后将其往后挪一个,指向下一个参数。
sum+=nArgValue;
}while(nArgValue!=0);
va_end(arg_ptr);//所有的形参遍历完,将arg_ptr指针置为NULL。
return sum;
}
int main()
{
int sum;
sum=add(1,2,3,0);//最后一个参数必须为0,因为add函数中需要以0来判断形参列表结束。
cout<<sum<<endl;
}
附加参考贴子:http://bbs.csdn.net/topics/80167059
typedef char * va_list;
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。
~是位取反的意思。
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。
~(sizeof(int) - 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) - 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) - 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) - 1) )后就正好将原长度补齐到4的倍数了。
数学解释:http://blog.sina.com.cn/s/blog_620508d80100f0wa.html
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
//作用是,将arg_ptr指向参数列表中的下一个元素的地址。
在调用va_start后,ap指向第一个可变参数。这个宏的作用就是在v的内存地址上增加v所占的内存大小,这样就得到了第一个可变参数的地址。
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
让我再来看看va_arg,它先ap指向下一个可变参数,然后减去当前可变参数的大小即得到当前可变参数的内存地址,再做个类型转换,返回它的值。
举个例子说明吧
int y=va_arg(x,int);
宏展开成( *(int *)((x += _INTSIZEOF(int)) - _INTSIZEOF(int)) )
此时x指向下一个参数(x = x + _INTSIZEOF(int))
然后x再减去_INTSIZEOF(int)得到x未改变前的地址,
再将x所指向的int类型的值赋给y
也就就是说y取得x所指向的int类型的值,然后x指向下一个参数地址
http://bbs.csdn.net/topics/100038782
这里ap指向下一个元素的地址,返回的是加了再减回去的地址的值。
#define va_end(ap) ( ap = (va_list)0 )
最后一个宏就简单了,va_end使得ap不再指向有效的内存地址。