浅析可变参数

在函数原型中,列出了函数期望的参数,但原型只能显示固定数目的参数,在求平均数的时候,如果数字有少数的几个,那么我们可以依次给函数传参,可是,如果数字多的话,难道要写出每个形参吗?那么让一个函数在不同时候接受不同数目的参数是否可以呢?答案是ok的,因为C语言很聪明,提供了一系列的可变参数来解决这个问题。

1,stdarg宏
可变参数可变参数列表是通过宏来实现的,这些宏定义在stdarg.h头文件中。这个头文

件声明了一个类型va_list和三个宏va_start,va_arg和va_end。

(1)va_list:声明变量,用于访问参数列表的未确定部分;

(2)va_start:初始化va_list申明的变量,它的第一个参数是va_list变量的名字,第二个参数是省略号前最后一个有名字的参数。通过初始化把va_list变量设置为指向可变参数部分的第一个参数;

(3)va_arg:用于访问参数,它的第一个参数是va_list变量的名字,第二个参数是参数列表中下一个参数的类型。在有些函数中,参数类型都是一样的,但有些函数可能要通过前面获得的数据来判断下一个参数的类型。va_arg返回这个参数的值,并使va_list变量指向下一个可变参数,如果此时已经是最后一个参数,则指向NULL。

(4)va_end:当访问最后一个可变参数之后,用va_end置空va_list变量.

注意:可变参数必须从头到尾按照顺序逐个访问,但也可以在访问几个参数之后半途中止。但是,不能从中间开始访问。此外,由于参数列表中的可变参数部分并没有原型,所以,所有作为可变参数传递给函数的值都将执行缺省参数类型提升。

2.代码实现:

实现可变参数函数:average,求平均值。

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.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 ret = 0;

   ret = average(5,1,2,3,4,5);
   printf("%d\n",ret);
   return 0;
}

实现可变参数函数:Max,求最大值。

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

int Max(int n,...)
{
    va_list arg;
    int i = 0;
    int max = 0;

    va_start(arg,n);//</span><span style="line-height: 26px; font-family: 宋体;"><span style="font-size:18px;">以固定参数的地址为起点确定可变参数的内存起始地址</span></span><span style="font-size:24px;">
    for(i=0; i<n; i++)
    {
        int tmp = 0;
        tmp = va_arg(arg,int); //得到下一个可变参数的值
        if(tmp > max)
            max = tmp;
    }
    va_end(arg);
    return max;
}

int main()
{
    int ret = 0;

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

3.可变参数在编译器中的处理

typedef char *  va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) (sizeof(int) - 1) )
#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap)      ( ap = (va_list)0 )

代码的含义:

(1)首先把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的

(2)定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替,对程序的运行结构也没有影响。(后文将看到我自己的实现)。

(3)va_start的定义为 &v+_INTSIZEOF(v)
,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap,v)以后,ap指向第一个可变参数在的内存地址.

(4)va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到下一个参数的起始地址。

(5)va_end宏的解释:ap=(char*)0.

4.实现简单的printf可变参数的函数。

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

int my_printf(char *val,...)
{
    va_list arg;
    va_start(arg,val);
    while(*val)
    {
        if(*val == 'c')
        putchar(va_arg(arg,char));//输出字符
        else if(*val == 's')
        puts(va_arg(arg,char*));//输出字符串
        val++;
    }
    va_end(arg);
}

int main()
{
    my_printf("s ccc","hello", 'b','i','t');
    return 0;
}

注意:如果在va_arg中指定了错误类型,其结果不可预测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值