va_arg,va_start,va_end 使用学习

来源:互联网 发布:上瘾网络剧网盘 编辑:程序博客网 时间:2024/06/05 22:57

对于 可变参数的使用,脚本语言python,ruby使用起来很方便 不过C、C++也有利器,那就是va_arg之类函数。

笔者每次使用的时候,都要查msdn的文档, 所以这次把它记录下来。 方便以后的查询。


首先是对可变参数处理的函数有哪些,需要哪些头文件。



其次, 各个函数分别是干什么的

void test(int i,....){   va_list vPtr; //申明一个可变的变量   var_start(vPtr,i); //将 vPtr指向可变变量的第一个变量      int n = va_arg(vPtr,int);//获得当时vPtr位置的变量,并将vPtr批向下一个位置。 这里下一个位置是由va_arg的第二个参数决定的。    va_end(vPtr);//在所有的变量处理完之后,将vPtr重置为NULL}


从这个函数的实现可以看到,我们使用可变参数应该有以下步骤: 
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变 
量是指向参数的指针. 
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第 
一个可变参数的前一个参数,是一个固定的参数. 
3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个 
参数是你要返回的参数的类型,这里是int型. 
4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使 
用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获 
取各个参数.

再次, 我们来看看可变参数在编译器中的处理

va_start, va_arg 和va_end是在stdarg.h中定义的, 由于硬件平台的不同与编译器的不同,所以定义的宏也有所不同,

以下,笔者会以VS2005中的定义为例。

在stdarg.h中,实际上再次引用vadefs.h中对他们的定义。


在vadef.h中, 对他们的定义如下

typedef char *  va_list;#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )#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 )

定义_INITSIZEOF(n)主要是为了某些为了需要内在的对齐的系统。

函数是从右向左压栈的, 下图画出了函数的参数在堆栈中的位置。


我们看到ADDRESSOF(v)是把v强制转换为char

_crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) 是获得当前固定参数的位置再加上参数所占的大小, 就指向了第一个可变变量的位置。

然后就是用va_arg来取得这个参数。

_crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

首先是ap+=sizeof(t),这里ap已经指向下一个参数的位置, 然后返回 ap-sizeof(t),这个正是第一个可变参数在堆栈中的位置。 再用*取得这个地址的内容。


最后是va_end,  _crt_va_end(ap)      ( ap = (char *)0 )这样使得ap不再指向堆栈.


最后,

由于是定义的宏, 所以 可变参数的个数, 参数的类型完全是由使用者来定义的, 它并不能智能的识别不同的参数的个数与类型。 

所以笔者在使用的过程中, 是模仿printf来写的, 就是在前面指定format的类型。 再用相应的类型来解析。

case 's': va_arg(arg, const char*); break;case 'i': va_arg(arg, int);         break;case 't': va_arg(arg, time_t);      break;case 'D': &va_arg(arg, time_t);      break;case 'T': va_arg(arg, time_t);      break;case 'f': va_arg(arg, double);      break;


原创粉丝点击