Analysis and application of variable parameter list

In the C language, by implementing the function as a variable parameter form, the function can be made to accept more than one arbitrary number of parameters (not fixed).

Let's look at an example and dissect it to understand variadic parameter lists

Compiler: vs2013

//使用可变参数,实现函数,求函数参数的平均值
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>

int average(int n, ...)
{
    va_list arg;
    int i = 0;
    int sum = 0;
    va_start(arg, n);
    for (i = 0; i < n; i++)
    {
        sum += va_arg(arg, int);
    }
    va_end(arg);
    return sum / n;
}

int main()
{
    int avg = average(4, 1, 2, 3, 5);
    printf("%d\n", avg);
    system("pause");
    return 0;
}

First, let's analyze part of the code

1.

int average(int n, ...) // ... 未知参数列表,n为未知参数列表个数
//将部分参数(1, 2, 3, 5)传给了未知参数列表

2.

code
va_list arg;
//char *arg 
//创建一个指向未知列表的指针
go to definition
typedef char *  va_list;
//右击鼠标,转到定义,可以观察到char *类型被重命名为 va_list

3.

code
va_start(arg, n);
//arg = (char *)(&(n)) + 4
//初始化arg为未知参数列表的第一个参数的地址
go to definition
#define va_start _crt_va_start
//宏,_crt_va_start被重命名为va_start
#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
//宏,( ap = (va_list)( &(v) ) + 4 )
#define _ADDRESSOF(v)   ( &(v) )
//宏,取v的地址
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//宏,当n所占的字节为1,2,3,4时,宏返回4;当n所占的字节为5,6,7,8时,宏返回8
//n所占的字节为4,宏返回4

4.

code
va_arg(arg, int);
//*(int *)((arg += 4) - 4)
//根据第二个参数在未知参数列表中获取一个参数,在获取一个参数之后,让arg向后移动指向下一个未知参数
go to definition
#define va_arg _crt_va_arg
//宏,_crt_va_arg被重命名为va_arg
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//宏,( *(t *)((ap += 4) - 4) )
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
//宏,当n所占的字节为1,2,3,4时,宏返回4;当n所占的字节为5,6,7,8时,宏返回8
//n所占的字节为4,宏返回4

5.

code
va_end(arg);
//arg = (char *)0
//把arg初始化为NULL
go to definition
#define va_end _crt_va_end
//宏,_crt_va_end被重命名为 va_end
#define _crt_va_end(ap)      ( ap = (va_list)0 )
//宏,( ap = (va_list)0 )

After that, we replace part of the code of the average function

int average(int n, ...)
{
    char *arg; //va_list arg;
    int i = 0;
    int sum = 0;
    arg = (char *)(&(n)) + 4; //va_start(arg, n);
    for (i = 0; i < n; i++)
    {
        sum += *(int *)((arg += 4) - 4); //sum += va_arg(arg, int);
    }
    arg = (char *)0; //va_end(arg);
    return sum / n;
}

From the above, we can see that

  • Declare a variable of type va_list for accessing the undetermined part of the argument list.
  • This variable is initialized with va_start, whose first parameter is the variable name of va_list, and the second parameter is the last named parameter before the ellipsis. The initialization procedure sets the arg variable to point to the first argument of the variable argument section.
  • In order to access the arguments, va_arg is used. This macro accepts two arguments: the va_list variable and the type of the next argument in the argument list. In the above example, all variadic parameters are integers. va_arg returns the value of this parameter and makes va_arg point to the next variable parameter.
  • Finally, after we have accessed the last parameter, we need to call va_end.

Applications of variable parameter lists

1. Use variable parameters, implement functions, and find the maximum value of function parameters

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

int Max(int n, ...)
{
    va_list arg;//创建一个指向未知列表的指针
    int i;
    int max = 0;
    va_start(arg, n);//初始化arg为未知参数列表的第一个参数的地址
    max = va_arg(arg, int); //获取第一个参数赋给max,之后,并且让arg向后移动指向下一个未知参数
    for (i = 1; i < n; i++)
    {
        int tmp = va_arg(arg, int);//获取一个参数赋给max,之后,并且让arg向后移动指向下一个未知参数
        if (max < tmp)
            max = tmp;
    }
    va_end(arg); //把arg初始化为NULL
    return max;
}

int main()
{
    int ret = Max(4, 1, 2, 3, 4);
    printf("%d\n", ret);
    system("pause");
    return 0;
}

Simulate a simple printf function

To simulate a simple printf function, you need to use the first parameter passed in to traverse the string. If you encounter characters such as d, c, s, etc. (simulate %d, %c, %s), use va_age obtains and outputs the parameters in the corresponding unknown parameter list. If it is not encountered, it will be output as it is.

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

void print(const char *format, ...)
{
    char *str = (char *)format; //保存
    va_list arg; //创建指向未知参数列表的指针
    va_start(arg, format);//初始化arg为可变参数列表的第一个参数的地址
    while (*str != '\0')
    {
        int ch;
        char *s;
        switch (*str)
        {
        case 'd':  //查到数字 print("%d\n", d);

            ch = va_arg(arg, int);
            while (ch > 10)
            {
                int d = 0;
                int i = 1;
                d = ch / 10;
                while (d > 10)//每次都得到ch第一位
                {
                    d = d / 10;
                    i++;
                }
                d = d + 48; //转化为字符
                putchar(d);
                i = (int)pow(10, i);
                ch = ch % i; //得到ch的后几位
            }
            ch = ch + 48;
            putchar(ch); //输出ch的最后一位
            str++;
            break;
        case 'c':  //查到单字符 print("%c\n", 'c');
            putchar(va_arg(arg, char));
            str++;
            break;
        case 's':  //查到字符串 print("%s\n", "s");,
            s = va_arg(arg, char*);//在未知参数列表中获取所对应字符串首字符的地址
            while (*s != '\0')
            {
                putchar(*s);
                s++;
            }
            str++;
            break;
        default: //查到其它时原样输出
            putchar(*str);
            str++;
            break;
        }           
    }
}

int main()
{
    print("s ccc d.\n", "hello", 'b', 'i', 't', 1234);
    system("pause");
    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325441745&siteId=291194637