可变参数列表的函数调用 va_list va_start va_arg va_end

时间:2022-07-07 19:39:55

    当无法列出函数所有形参的类型和数目时,可用省略号指定参数表,例如:

           void foo(...);
           void foo(parm_list,...);

下面介绍这种可变参数列表的函数该如何处理传递来的参数。

 

1. 函数参数的传递原理

    函数参数是以数据结构——栈的形式存取,参数从右至左入栈。eg:

                    void func(int x, float y, char z);
调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z。因此,从理论上说,只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,总可以找到其他的输入变量。

 

2. 处理可变参数列表的宏

在<stdarg.h> 里定义了几个重要的处理可变参数列表的宏:

typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );

 

va_list  是一个字符指针类型,用于定义指向省略号"..." 所代表的参数列表的指针,如定义: 

                               va_list  ap;

va_start 用于初始化上面定义的指针,使其指向"..." 参数列表的起始地址,va_start的第二个

         参数就是邻近"..." 参数列表的参数,用于定位"..." 参数列表;

va_arg   用于获取参数,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回

         这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;

va_end   将参数ap 置为 NULL,关闭指针,以免产生悬指针。

 

3. 示例

void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
{
  va_list args;
  va_start(args, pszFormat);    //一定是邻近“...”的那个参数
  _vsnprintf(pszDest, DestLen, pszFormat, args);
  va_end(args);
 }

例中函数_vsnprintf用于输出 "..." 列表参数,其说明如下:

  头文件        

    #include <stdarg.h>

  函数声明:

  int _vsnprintf(char *buffer, size_t max_count, const char *format, va_list vArgList);

  参数说明:

  1). char *buffer [out],把生成的格式化的字符串存放在这里.

  2). size_t max_count [in], buffer可接受的最大字节数,防止产生数组越界.

  3). const char *format [in], 格式化字符串

  4). va_list vArgList [in], va_list变量. va:variable-argument:可变参数

  用法类似于vsprintf,只不过加了max_count的限制.

  返回值说明:

  如果成功调用此函数,返回写到buffer中的字符的个数(不包括结尾的'\0')。snprintf和vsnprintf函数不能够写多于size大小(包括结尾的'0')的字节数。如果输出因为以上原因被截断,返回成功写入buffer的字符数(不包括结尾的'\0'),如果有足够的内存空间的话。所以,如果返回值等于size或者大于size,表示输出到buffer的字符被截断,如果输出过程中遇到错误,则返回一个负数。