1、va_arg, va_end, va_start的用法用结

简介:     今天看别人写的代码,其中涉及有va_arg, va_end, va_start部分。于是翻阅了一些资料,总结了一下。  由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;于是人们想出了用指针参数来解决问题。

    今天看别人写的代码,其中涉及有va_arg, va_end, va_start部分。于是翻阅了一些资料,总结了一下。 

由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;于是人们想出了用指针参数来解决问题。关于这一部分标准库的定义,VSunix定义的是不一样的,可以参见MSDN关于这一部分的解释。 

type va_arg( va_list arg_ptr, type ); 

void va_end( va_list arg_ptr ); 

void va_start( va_list arg_ptr ),  (UNIX version) 

void va_start( va_list arg_ptr, prev_param );   (ANSI version)

MSDN中)

其中的参数解释如下:

type Type of argument to be retrieved

arg_ptr Pointer to list of arguments

prev_param Parameter preceding first optional argument (ANSI only)

va_arg Macro to retrieve current argument

va_end Macro to reset arg_ptr

va_list typedef for pointer to list of arguments defined in STDIO.H

va_start Macro to set arg_ptr to beginning of list of optional arguments (UNIX version only)

VC中,其定义如下:

typedef char *  va_list;

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

#define va_start(apv) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一个可选参数地址

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

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

网友于上述程序中关键地方的解释:

在进程中,堆栈地址是从高到低分配的。当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减。

总之,函数在堆栈中的分布情况是,地址从高到低,依次是:函数参数列表,函数返回地址,函数执行代码段。

堆栈中,各个函数的分布情况是倒序的。即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分。参数在堆栈中的分布情况如下:

最后一个参数

倒数第二个参数

...

第一个参数

函数返回地址

函数代码段

主要函数介绍:

va_start 使argp指向第一个可选参数。va_arg返回参数列表中的当前参数并使argp指向参数列表中的下一个参数。va_endargp指针清为 NULL。函数体内可以多次遍历这些参数,但是都必须以va_start开始,并以va_end结尾。

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_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.

  After all arguments have been retrieved, va_end resets the pointer to NULL

示例代码

 

代码
 
   
1 #include < stdio.h >
2   #define ANSI /* Comment out for UNIX version */
3 #ifdef ANSI /* ANSI compatible version */
4 #include < stdarg.h >
5 int average( int first, ... ); /* ANSI compatible version */
6 int average( int first, ... )
7 {
8 int count = 0 , su
9 m = 0 , i = first;
10 va_list marker;
11
12 va_start( marker, first ); /* Initialize variable arguments. */
13 while ( i != - 1 )
14 {
15 sum += i;
16 count ++ ;
17 i = va_arg( marker, int );
18 }
19 va_end( marker ); /* Reset variable arguments. */
20 return ( sum ? (sum / count) : 0 );
21 }
22 #endif
23 void main( void )
24 {
25 /* Call with 3 integers (-1 is used as terminator). */
26 printf( " Average is: %d\n " , average( 2 , 3 , 4 , - 1 ) );
27
28 /* Call with 4 integers. */
29 printf( " Average is: %d\n " , average( 5 , 7 , 9 , 11 , - 1 ) );
30
31 /* Call with just -1 terminator. */
32 printf( " Average is: %d\n " , average( - 1 ) );
33 }
说明:我的 VS2005头文件中宏定义如下:

 

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#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 )

其中:

#ifdef __cplusplus

#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )

#else

#define _ADDRESSOF(v) ( &(v) )

#endif

***********

#define va_start _crt_va_start

#define va_arg _crt_va_arg

#define va_end _crt_va_end

参考

[1] MSDN

[2] http://topic.csdn.net/u/20100711/15/C28B9247-D381-4525-92C0-6725BDB34A08.html

[3] http://www.cnblogs.com/wubiyu/archive/2008/07/30/1256860.html

 

 

目录
相关文章
End Sub 和 Exit Sub 的区别
End Sub 和 Exit Sub 的区别
145 0
End Sub 和 Exit Sub 的区别
Data Vault的一点思考(三)
前段时间和一家互联网创新企业的数据架构师聊天,说他们正准备开始用Data Vault的模型进行数据仓库的建设,他们有一套维度建模的集市,但是维度建模的特点是更贴业务,但是对于数据的存储不是很友好,对于数据友好,他们是想在维度集市之前,加一层Data Vault的数据层。
2529 0
|
数据建模 大数据
Data Vault的一点思考(二)
现在的企业,只要是有点规模的,已经不仅仅是有ER这一套数据仓库模型来做数据,如现在很多企业从原有的线下转到线上,就不得不面临DM这一套数据模型,或者是线下和线上都有,而且是相互促进的(个人认为这是发展趋势的必然),但是貌似来说现阶段线上企业用DM这一套模型,现在更火,因为现在还是处在线下往线上发展的阶段,很多创业公司只要是线上的,数据的分析是必然,那怎么把数据组织好把数据给下游用,现在的主流是DM。
2899 0
|
缓存 NoSQL Redis
BITCOUNT key [start end]
统计字符串被设置为1的bit数. 一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。 start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。
1349 0