可变参数列表源码的剖析及应用

我们在自己写一个函数时,我们发现:函数的传参参数是有限的,当你要传几个参数时,定义函数接收时就要定义几个形式参数。这个方法是十分麻烦的!

//举个例子:实现一个函数可以求任意个参数的平均值
#include<stdio.h>
int ave(int n,int i,int j,int k)//创建4个形参
{
    int ave=0;
    return ave=(i+j+k)/n;
}
int main()
{
    printf("ave=%d\n",ave(3,3,4,5));//传4个参数
    return 0;
}

那么有没有一种方法解决函数可以接受1个以上的任意多个参数(不固定)?
这就是可变参数列表!!!

#include<stdio.h>
int ave(int n,...)//"..."代表参数未知
{
    va_list arg;
    //typedef char *  va_list;
    int i=0;
    int sum=0;
    _crt_va_start(arg,n);
    //#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    //#define _crt_va_start(ap,v)  ( ap = (va_list)( &(v) ) + _INTSIZEOF(v) )
    for(i=0;i<n;i++)
    {
        sum+=_crt_va_arg(arg,int);
        //#define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    }
    _crt_va_end(arg);
    //#define _crt_va_end(ap)      ( ap = (va_list)0 )
    return sum/n;
}
int main()
{
    printf("ave=%d\n",ave(3,3,4,5));
    return 0;
}


上面这段代码用了4处宏,什么意思呢?

  • va_list arg : 创建一个字符指针arg,用于访问参数列表的未确定部分。

  • _crt_va_start(arg,n) : 初始化变量arg。第一个宏定义可以理解为求n类型的大小,由于定义为int型,故为4;第二个宏定义,我们把宏带入得到arg=(char *)(&arg)+4,把arg变量设置为指向可变参数部分的第一个参数。

  • _crt_va_arg(arg,int) :访问arg变量和参数列表中下一个参数的类型。
    由于例子中可变参数都是整型,那么代码为 (int )((arg+=4)-4),表示返回这个参数的值,并自动指向下一个参数。

  • _crt_va_end(arg) :当访问完毕最后一个可变参数之后,需要销毁指针变量arg,释放空间,相当于 arg=NULL。

当使用可变参数时也有一定限制:

  • 可变参数必须从头到尾逐个访问。如果在访问几个参数后想半途终止,这是可以的;但是,如果不能一开始就访问列表中间的参数。

  • 参数列表中至少有一个命名参数。

  • 在代码中出现的宏是无法直接判断实际存在参数的个数及类型。

  • 如果在指定未知参数,即_crt_va_arg第二个参数指定错了类型,那么由于指向的地址错误,其取出的值也是错误的,最后结果是不可预测的。

了解清楚代码意思后,做几个小练习

习题1:实现一个函数可以求任意个参数的平均值


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

double average(int n , ...)
{
    va_list arg;
    int i = 0;
    double sum = 0;
    _crt_va_start(arg, n);
    for (i = 0; i < n; i++)
    {
        sum += _crt_va_arg(arg, double);
    }
    _crt_va_end(arg);
    return sum / n;
}
int Average(int n, ...)
{
    va_list arg;
    int i = 0;
    int sum = 0;
    _crt_va_start(arg, n);
    for (i = 0; i < n; i++)
    {
        sum += _crt_va_arg(arg, int);
    }
    _crt_va_end(arg);
    return sum / n;
}
int main()
{
    printf("average=%d\n", Average(3, 1, 2, 3));
    printf("average=%lf\n", average(3, 1.5, 2.5, 5.0));
    system("pause");
    return 0;
}

习题2:实现一个函数可以比较任意个参数的大小


#include <stdio.h>
#include <stdlib.h>
int Max(int n, ...)
{
    va_list arg;
    int i = 0;
    int max = 0;
    int tmp = 0;
    _crt_va_start(arg, n);
    max = _crt_va_arg(arg, int);
    for (i = 1; i < n; i++)
    {
        tmp = _crt_va_arg(arg, int);
        max = max>tmp ? max : tmp;
    }
    _crt_va_end(arg);
    return max;
}
double MAX(int n, ...)
{
    va_list arg;
    int i = 0;
    double max = 0;
    double tmp = 0;
    _crt_va_start(arg, n);
    max = _crt_va_arg(arg, double);
    for (i = 1; i < n; i++)
    {
        tmp = _crt_va_arg(arg, double);
        max = max>tmp ? max : tmp;
    }
    _crt_va_end(arg);
    return max;
}
int main()
{
    printf("max=%d\n", Max(3, 1, 7, 3));
    printf("max=%lf\n", MAX(3, 2.0, 4.0, 6.0));
    system("pause");
    return 0;
}

习题3://模拟实现printf函数,能完成下面函数的调用。 print(“s ccc d.\n”,”hello”,’b’,’i’,’t’,100);


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int print_num(int n) //使用递归打印整型数
{
    if (n > 9)
        print_num(n / 10);
    return  putchar(n % 10 + '0');  //1+'0'='1'
}
void print(char *format, ...)
{
    va_list arg;
    char *p = NULL;
    assert(format != NULL); //断言指针变量format不为空指针
    _crt_va_start(arg, format);
    while (*format != '\0') 
    {
        if (*format == 's')
        {
            p = (_crt_va_arg(arg, char *));
            while (*p)
                putchar(*p++);
        }
        else if (*format == 'c')
            putchar(_crt_va_arg(arg,char));
        else if (*format == 'd')
        {
            int num=(_crt_va_arg(arg, int));
            print_num(num);
        }
        else
            putchar(*format);
        format++;
    }
    _crt_va_end(arg);
}
int main()
{
    print("s ccccc , d.\n", "hello", 'C', 'h', 'i','n','a', 2018);
    system("pause");
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值