C/C++:va_arg, va_copy, va_end, va_start 宏的使用

2015-11-21

va_arg, va_copy, va_end, va_start 这几个宏可以访问变量参数列表。对于参数个数未知的函数,可以用这几个宏来创建输入参数的 token,以使用这些参数。

语法如下:

type va_arg(
    va_list arg_ptr,
    type 
);
void va_copy(
    va_list dest,
    va_list src
); // (ISO C99 and later)
void va_end(
    va_list arg_ptr 
);
void va_start(
    va_list arg_ptr,
    prev_param 
); // (ANSI C89 and later)
void va_start(
    arg_ptr 
);  // (Pre-ANSI C89 standardization version)

主要功能:

  • va_start 将 arg_ptr 设置为传递给该函数参数列表的第一个可选参数。 参数 arg_ptr 必须具有 va_list 类型。 参数 prev_param 是参数列表中紧邻第一个可选参数列的必选参数的名称。 如果 prev_param 声明为注册存储类,则宏的行为未定义。 va_start 必须在 va_arg 第一次使用前使用。

  • va_arg 从 arg_ptr 给出的位置检索 type 的值,并将 arg_ptr 指向列表中的下一个参数,使用 type 的范围确定下一个参数开始的位置(因此指定 type 很重要)。 va_arg 在该函数可以使用任意数量的时间从列表中检索实参。

  • va_copy 复制了其当前状态下的参数列表。

  • 在所有参数检索之后,va_end 重置指向 NULL的指针。 在函数返回之前,必须在初始化 va_start 或 va_copy 的每个参数列表之上调用 va_end 。

测试代码如下(使用 Visual Studio 2013):

#include <stdio.h>
#include <stdarg.h>

void testit(int i, ...)
{
    va_list argptr;
    va_start(argptr, i);

    if (i == 0)
    {
        int n = va_arg(argptr, int);
        int m = va_arg(argptr, int);
        printf("%d\n", n);
        printf("%d\n", m);
        va_end(argptr, i);

        va_start(argptr, i);
        int n2 = va_arg(argptr, int);
        int m2 = va_arg(argptr, int);
        printf("%d\n", n2);
        printf("%d\n", m2);
        va_end(argptr, i);
    }
    else
    {
        char *s = va_arg(argptr, char*);
        printf("%s\n", s);
    }
}

int main()
{
    testit(0, 999, 444); // 1st problem: 0xffffffff is not an int
    char h[] = "saaa";
    testit(1, h);       // 2nd problem: NULL is not a char*
    getchar();
}

更复杂的测试代码(来自 MSDN 官方文档):

// crt_va.c
/* Compile with: cl /W3 /Tc crt_va.c
* The program below illustrates passing a variable
* number of arguments using the following macros:
*      va_start            va_arg              va_copy
*      va_end              va_list
*/

#include <stdio.h>
#include <stdarg.h>
#include <math.h>

double deviation(int first, ...);

int main( void )
{
    /* Call with 3 integers (-1 is used as terminator). */
    printf("Deviation is: %f\n", deviation(2, 3, 4, -1 ));

    /* Call with 4 integers. */
    printf("Deviation is: %f\n", deviation(5, 7, 9, 11, -1));

    /* Call with just -1 terminator. */
    printf("Deviation is: %f\n", deviation(-1));
}

/* Returns the standard deviation of a variable list of integers. */
double deviation(int first, ...)
{
    int count = 0, i = first;
    double mean = 0.0, sum = 0.0;
    va_list marker;
    va_list copy;

    va_start(marker, first);     /* Initialize variable arguments. */
    va_copy(copy, marker);       /* Copy list for the second pass */
    while (i != -1)
    {
        sum += i;
        count++;
        i = va_arg(marker, int);
    }
    va_end(marker);              /* Reset variable argument list. */
    mean = sum ? (sum / count) : 0.0;

    i = first;                  /* reset to calculate deviation */
    sum = 0.0;
    while (i != -1)
    {
        sum += (i - mean)*(i - mean);
        i = va_arg(copy, int);
    }
    va_end(copy);               /* Reset copy of argument list. */
    return count ? sqrt(sum / count) : 0.0;
}

参考:MSDN


标签: C/C++

留言请用 Github Issues

聊天请在 Gitter/fan-farm

授权协议 (CC) BY-NC-SA | 订阅 RSS | 邮箱 hi@fzheng.me