C++查漏补缺——可变参数的初步理解

可变参数:

含义:函数参数个数可以变,在C#中有专门的关键字parame。

格式:用三个点“...”来表示,类型可以是int、double、char*、类、结构体等等。

 

变参宏(Variadic Macros):

void func1(char nparam, ...){

    return;

}

#define func2(...) {func1(...);}

编译的时候会提示func2为非法表达式。

#define func3(...) {func1(__VA_ARGS__);}

__VA_ARGS__是变参宏。表示前面定义的"..."。只有三点用在宏定义上,__VA_ARGS__才有效。

 

三点”...“的操作:

(环境:

va_list

可以在vadefs.h找到其定义。

typedef char *  va_list;

 

va_start、va_end、va_arg

可以在stdarg.h找到其定义。

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )                                //获取第一参数的地址,ap为取出数据的指针,v为数据源的指针

#define va_end(ap)      ( ap = (va_list)0 )                                                         //将指针置无效

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )         //下一个参数的地址,

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )              //计算参数地址间间隔大小

 

这里要注意面对不同类型,用法也有一定的差异。

假设传入的int类型:

定义:

void func_Num(int nSrc,...)

{

     va_list cl;

     va_start(cl, nSrc);

     printf("Num:%d\n", *cl);

     va_arg(cl, int);

     printf("Num:%d\n", *cl);

     va_end(cl);

}

调用:

func_Num(12,23,34,45);

 

但是如果传入的参数是char*类型:

定义:

void func_Charp(char* chSrc,...)

{

     printf("->:%d\n", chSrc);

     va_arg(cchSrc, char*);

     printf("->:%d\n", chSrc);

     va_arg(chSrc, char*);

     printf("->:%d\n", chSrc);

}

调用:

func_Charp("abc", "def", "ghi");

解析一下:

先以func_Num为例:

(1)参数列表存入的是一个栈结构,自第一个元素nSrc元素起,元素地址间的偏移量为_INTSIZEOF(type)。

(2)va_start(cl, nSrc)等价与cl= (va_list)&nSrc + _INTSIZEOF(nSrc),由于nSrc就是第一个参数,即(va_list)&nSrc表示的是第一个参数的地址,加上_INTSIZEOF(type)的偏移量之后便是第二个元素的地址,于是函数中第一次调用va_start获得就是参数列表的第二个函数,即”...“所表示的第一个参数,如果没有chSrc,意味没有第一个元素的位置可参考,无法拿到下一个元素的位置。

为什么存在不同就需要理解参数的存储。