C语言可变参数列表解析

可变参数列表的解析

函数中除了能传入固定数目的参数,还能接受数量不定的参数。

可变参数列表是通过宏来实现的,这些宏定义于stdarg.h 头文件,它是标准库的一部分。

这个头文件声明了一个类型  va_list  和三个宏  va_start  va_arg  和  va_end.

通过可变参数列表计算多个参数的平均值

#include <stdarg.h>
#include <stdio.h>
float average(int n_values, ...){
    va_list arg;    //设置访问参数列表的变量
    int i = 0;
    float sum = 0;
    va_start(arg, n_values);   //对可变参数列表进行访问
    for (i = 0; i < n_values; i++)
        sum += va_arg(arg,int);  //获取可变参数列表的值
    return sum / n_values;
    va_end(arg);
        
}
int main()
{
    int a = 1, b = 2, c = 3, d = 4;
    int avg1 = average(2, a, c);
    int avg2 = average(3, a, b, c);
    printf("%d\n", avg1);
    printf("%d\n", avg2);
    return 0;
}

转到函数的源码

typedef char *  va_list;//va_list 是声明的char*的指针类型


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

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
//将声明的arg变量指向可变参数部分的第一个参数
#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
//取出每一个参数,t表示下一个参数的类型
#define _crt_va_end(ap)      ( ap = (va_list)0 )

va_start

函数声明了一个va_list 类型的变量,用于访问参数列表的未确定的部分。这个变量通过调用va_start进行初始化。va_start(arg , n_values)  它的第一个参数为声明的变量arg ,第二个参数为省略号前最后一个有名字的参数。初始化过程把arg变量设置为指向可变参数部分的第一个参数。

va_arg

这个宏用于访问参数,它接受两个参数:va_list变量和参数列表下一个参数的类型。本例中所有参数都为整型,,但在有些函数中,可能需要通过前面获取的数据来判断下一个参数的类型,例如(printf检查格式字符串中的参数来判断它需要打印的参数的类型)。

va_arg 返回这个参数的值,并使变量arg指向下一个参数。

va_end

当访问完最后一个可变参数之后,我们需要调用va_end。

可变参数的限制

可变参数必须从头到尾按顺序逐个访问,它可以在访问途中停止。但是不能一开始就从中间访问参数。
由于参数列表中的可变参数部分并没有原型,所以,所有作为可变参数传递给函数的值都将执行缺省参数类型提升。
参数列表中至少要有一个命名参数。否则将无法通过va_start查找到可变参数部分。

对于这些宏存在两个基本的限制

  1. 这些宏无法判断实际存在参数的数量。

  2. 这些宏无法判断每个参数的类型。

要通过使用命名参数才能解决这两个问题,在求平均值的程序中,命名参数指定了可变参数的数量,他们的类型被假定为整型。printf函数中的命名参数是格式字符串,它不仅指定了参数的数量还指定了参数的类型。

#利用可变参数列表求解最大值

#include <stdarg.h>
#include <stdio.h>
int max(int n, ...)
{
    va_list arg;
    va_start(arg, n);
    int max = va_arg(arg, int);
    int t = 0;
    n--;
    while (n--){
    t = va_arg(arg, int);
        if (max < t)
            max = t;
    }
    return max;

}
int main()
{
    int a = 1, b = 2, c = 3;
    int ret=max(3, a, b, c);
    printf("%d", ret);
    return 0;
}
##利用可变参数列表实现printf

#include <stdarg.h>
#include <stdio.h>
void printfnum(int num)
{
    if (num > 9)
        printfnum(num/10);
    
    putchar(num % 10 + '0');

}
print( const char *format, ...)
{
    va_list arg;
    va_start(arg, format);
    while (*format)
    {

        switch (*format)//首先判断字符的类型,然后将其打印
        {
        case 's':
        {
                    char *str = format;
                    str = va_arg(arg, char*);
                    while (*str)
                    {
                        
                        putchar(*str);
                        str++;
                    }
        }
            break;
        case 'c':
        {
                    char ch = va_arg(arg, char);
                    putchar(ch);
        }
            break;
        case 'd':
        {
                    int ret = va_arg(arg, int);
                    printfnum(ret); //将数字100通过字符的方式一个个的打印出来
        }
            break;
        default:
            putchar(*format);
            break;
        }
        format++;
    }
}

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



猜你喜欢

转载自blog.csdn.net/m_jianjianjiao/article/details/80357660