前言:
c/c++的默认调用协定是 “从右向左遍历参数列表的方式压栈” ,并且回收栈空间的动作由函数调用者完成,对应的前缀是 _cdecl ,那么我们便可以利用这一特性,在函数的参数列表中使用 ... 来表示此处有 n 个入参,具体数量由调用者决定。这便是传统的动态参数列表实现方式。
c++ 11提供了更加便捷的 initializer_list<T> 来完成上述任务。
传统的动态参数列表:
#include <stdio.h>
#include "stdarg.h"
void myfunc(int argnum,...)
{
va_list args; //va_list 可以理解为一个指针
va_start(args,argnum); //此宏需要知道最后一个传递给自己的参数是谁
int i;
for(i=0;i<argnum;i++){
int j;
j=va_arg(args,int); //以int类型为模板,读取当前args所指位置的值
printf("%d\n",j);
}
va_end(args);
}
int main(int argc,char ** argv)
{
myfunc(5,1,2,3,4,5);
}
有几点需要注意:
1)myfunc的参数列表不能只是...,一定要有第一个参数,这样才能定位函数调用栈的范围;
2)... 中的数据类型需要和 va_arg 的第二个参数匹配,否则在解析是便会出错,比如 传入int,但是va_arg解析char,这样便会根据机器的大小端情况做一定的数据截取,同时剩下的数据也将错位;
3)printf函数的第一个参数是用的字符串,然后使用%进行限制,这样其实现里只需要找到%,然后读取后一个字符,再进行switch判断,根据switch判断的类型决定 va_arg 的第二个参数使用什么
使用 initializer_list<T> 的动态参数列表:
这种方法,要求可变参数的类型一致,initializer_list<T>和vector<T>类似,只不过initializer_list里的值只能是常量,不可在函数中修改,例:
void func(initializer_list<int> il); //在函数实现中,il的使用和vector一样
func({1,2,3,4,5});
...
void func(initializer_list<int> list)
{
foreach(auto ele:list)
{
....
}
}
注意:因为 initializer_list 是模板函数,因此其参数类型必须保持一致,这点是一个局限性,灵活性而言,传统动态参数列表更加灵活