天天看點

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

    今天看别人寫的代碼,其中涉及有va_arg, va_end, va_start部分。于是翻閱了一些資料,總結了一下。 

由于在C語言中沒有函數重載,解決不定數目函數參數問題變得比較麻煩;于是人們想出了用指針參數來解決問題。關于這一部分标準庫的定義,VS和unix定義的是不一樣的,可以參見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(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一個可選參數位址

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一個參數位址

#define va_end(ap) ( ap = (va_list)0 ) // 将指針置為無效

網友于上述程式中關鍵地方的解釋:

在程序中,堆棧位址是從高到低配置設定的。當執行一個函數的時候,将參數清單入棧,壓入堆棧的高位址部分,然後入棧函數的傳回位址,接着入棧函數的執行代碼,這個入棧過程,堆棧位址不斷遞減。

總之,函數在堆棧中的分布情況是,位址從高到低,依次是:函數參數清單,函數傳回位址,函數執行代碼段。

堆棧中,各個函數的分布情況是倒序的。即最後一個參數在清單中位址最高部分,第一個參數在清單位址的最低部分。參數在堆棧中的分布情況如下:

最後一個參數

倒數第二個參數

...

第一個參數

函數傳回位址

函數代碼段

主要函數介紹:

va_start 使argp指向第一個可選參數。va_arg傳回參數清單中的目前參數并使argp指向參數清單中的下一個參數。va_end把argp指針清為 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、va_arg, va_end, va_start的用法用結
1、va_arg, va_end, va_start的用法用結

代碼

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

繼續閱讀