C语言函数的不定参数
veekxt 发布于 2015-06-08 00:00:00

c语言可以定义不确定的参数列表,典型的例子就是库函数printf()系列,不仅参数个数,参数类型也是不定的。但是它至少包含一个确定的参数,当然也可有多个,定义一个不定参函数就像这样:

int foo(int arg1,...)
{
//code in here
return 0;
}

省略号表示之后的参数不确定。那么问题来了,不定参关键的问题就是如何读取每一个参数并且确定类型,c标准库定义了以下数据类型和函数(看起来是函数,一般是宏)来解决这个问题:

va_list

一个指针类型,用来指向参数的。

va_start(va_list ap,type_t arg1)

初始化,使ap指向可变列表的第一个参数,arg1应该是固定参数中的最后一个。不知道标准有没有定义返回值,不用在意这个。

va_arg(va_list ap,type_t)

读取ap指向的参数,使用第二个参数说明参数的类型,并且使ap指向下一个参数。

va_end(va_list ap)

释放ap指针。没什么好说的。

那么问题又来了,上面的过程说明程序可以通过不断用va_arg()来取参数,那么怎么判断什么时候参数取完呢?那就是用户自己决定,完全可以自己决定读两个,读三个... ...不过稍微思考一下就能发现我们不是有至少一个确定的参数吗?我们把剩余的参数个数,甚至类型信息,写到确定的参数里不就行了吗,实际上printf()不就是这么做的吗,每遇到一个%就读取一个参数,而%之后的一个字符记录了类型信息。
自己实现一个函数,来求各个参数的和:

int foo(int cnt,...)//cnt表示后面有几个参数,cnt不是参与相加的
{
    int sum=0;
    va_list ap;
    va_start(ap,cnt);
    for(int i=0;i<cnt;i++)
    {
        sum=sum+va_arg(ap,int);//取出参数累加
    }
	va_end(ap);
    return sum;
}

然后就可以这样调用了:foo(3,1,4,7);foo(2,56,78);等等。

这样的不定参数函数的缺点就是一定要有确定的参数来说明以后的参数信息,不然就不知道怎么取和取多少参数了。

最后贴一下某版本mingw里的实现(X86),就不分析原理了。

typedef char *  va_list;
#define _ADDRESSOF(v) (&(v))
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
#define _crt_va_start(v,l)	((v) = (va_list)_ADDRESSOF(l) + _INTSIZEOF(l))
#define _crt_va_arg(v,l)	(*(l *)(((v) += _INTSIZEOF(l)) - _INTSIZEOF(l)))
#define _crt_va_end(v)		((v) = (va_list)0)