深度解析可变参数列表

可变参数列表解析:

可变参数是指,函数部分的参数个数不是固定的,每次根据需求可以传入不同个数的参数。实际上,printf()函数;就是一个典型的可变参数函数,可以打印一个字符串,也可以打印多个字符串。

例如:

int main()
{
    printf("%d\n ",2);                                 //有一个参数
    printf("%s %d\n","sunshine",3);                    //有两个参数
    printf("%s  %s  %d\n","sunshine", "girl",3);       //有三个参数
    system("pause");
    return 0;
}

结果:这里写图片描述

下面以一个求平均值的代码来详细解释可变参数列表:
#include<stdarg.h>
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 av = average(3, 4, 5, 6);
    printf("%d\n", av);   //5
    av = average(4, 2,4,6,8);//5
    printf("%d\n", av);
    system("pause");
    return 0;
}

下面对是对以上代码的解释:

#include<stdarg.h>   //可变参数对应的头文件
average(int n, ...)   // 不知道几个参数,所以用...表示,叫做未知参数列表,n代表后面有多少个参数
{
    va_list arg;       
    // char* arg
    int i = 0;
    int sum = 0;
    va_start(arg, n);
    //va_start_a(arg, n)   ((void)(arg = (cha*)&(n) + _INTSIZEOF(n)))   // 向上取一个整型的大小  如果n是一个字节,则 _INTSIZEOF(n) 为4
    //( arg = (char*)&(n) + 4 )   // 函数调用过程中,由于在栈空间开辟的过程中,先将函数参数右边的参数压栈,所以栈顶上的元素,是传的参数个数,因此,arg指向未知参数中的第一个参数 
    for (i = 0; i < n; i++)
    {
        sum += va_arg(arg, int);
        // va_arg(arg, int)   ( *(int*)((arg += 4 ) - 4 ))
        //sum = sum + ( *(int*)((arg += 4 ) - 4 ))  //这句代码写的相当巧妙,完成了两个动作。
        //1.arg移动4个字节,指向未知参数的下一个元素,再赋给arg,使得arg的位置发生改变  
        //2.同时 ((arg += 4 ) - 4 ) 中 要注意-4,回到了第一个元素的位置,但是由于没有赋值,arg所指的仍然是下一个位置
    }   
    va_end(arg);   
    //#define va_end(arg)    ((void)(arg = (char*)0))
    //((void)(arg = (char*)0))   
    return sum / n;
} 


int main()
{

    int av = average(3, 4, 5, 6);
    printf("%d\n", av);   //5
    av = average(4, 2,4,6,8);//5
    printf("%d\n", av);
    system("pause");
    return 0;
}
  • 声明一个va_list 类型的变量 arg,它用于访问参数列表的未确定部分。
  • 这个变量是调用 va_start 来初始化的。它的第一个参数是va_list的变量名,第二个参数是省略号前面最后一个有名字的参数(因为对于不同的函数来说,省略号之前可能会有多个参数)。初始化过程把arg变量设置为指向可变参数部分的第一个参数。
  • 为了访问参数,需要使用va_arg,这个宏接收两个参数:va_list变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。va_arg返回这个参数的值,并使用va_arg指向下一个可变参数。
  • 最后,当访问完毕最后一个可变参数之后,我们需要调用va_end

可变参数列表的练习:

模拟实现库函数中的 printf() 函数,函数原型为 print(char *format, …)

void show(int n)
{
    if (n > 9)
    {
        show(n / 10);  //递归,拆分整型每一位
    }
    putchar(n % 10 + '0');   //整型+'0' 结果是 整型对应的字符,比如,整型4+'0' 对应是字符'4'
}


print(const char *format, ...)
{
    va_list arg;  
    va_start(arg, format);   //让arg指向未知参数列表前第一个有名字的参数(format),初始化过程
    while (*format)
    {
        switch (*format)
        {
              case 's':   //遇到s 说明是个字符串
                    {
                          char* ret = va_arg(arg, char*);  //取出的是 字符串首字符的地址
                          while (*ret)
                                {
                                     putchar(*ret);
                                     ret++;
                                 }
                    }
                   break;
        case 'd':      //遇到d 说明是个整型
                    {
                           int ret = (int)va_arg(arg, int);
                           show(ret);
                    }
                    break;
        case 'c':
                    {
                           char* ret = va_arg(arg, char*);
                           putchar(ret);
                    }
                    break;
        default:     // 既不是字符串,也不是整型,字符,就正常输出
                   {
                           putchar(*format);
                   }
                   break;
        }

        format++;
    }

    va_end(arg);
}

int main()
{
    print("s cccc d !~~\n", "beautiful", 'c', 'o','o', 'l',100);  //函数传参,需要传类型 和 内容
    system("pause");
    return 0;
}

输出结果:这里写图片描述

ps:补充一个知识点:库函数中printf() 函数的返回类型 是整型,返回值是打印在屏幕上字符的个数。

可变参数的注意事项:

  • 可变参数必须从头逐个访问。允许在访问了几个可变参数之后在中途停止,但是,不允许一开始就访问参数列表中间的参数。
  • 参数列表中至少有一个命名参数。如果连一个命名参数都没有,那就无法使用va_start。
  • 这些宏是无法直接判断每个参数的数量。
  • 这些宏无法判断每个参数的类型。
  • 如果在va_arg中指定了错误的类型,它就会按照错误的类型的往下查找,结果肯定是错误的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值