可变参数列表剖析

如果我同时想求两个整型变量的平均值,3个整型变量的平均值,4个,5个,那我同时要写多少个函数,来完成同一个功能。其实在c语言库中,很多函数的参数变量都是可变的。不是确定的。比如printf。你能说出printf的变量都有啥不。好像不可以,因为我们每次使用printf时,给的变量都是不一样的。有的时候,只有“aaaaaaaaaaaa”这样的字符常量。有的时候还有(“%d”,1).%c,总是不同的对吧。

     我们看一下printf函数的源代码

  int printf(const char *format, ...)

    {   }

 

       在这个printf函数中,首先我们看见她第一个参数是(const char*format),然后则是“   . . .  ”,我们不难看出这"  . . . "代表的就是可变参数。在我的上一篇博客中,详细讲解了栈帧这一概念。也知道了,参数存在哪个位置,而且是从右向左存的。  也就是说,只要我们找到左边明确已知的的参数,我们就可以通过地址++,访问其他的参数。右边--。因为从右向左存。所有b,a,num的地址依次减小。

 

aver(num,a,b);

 

                        b
                        a       
                     num

 

 

 

让我们试着用可变参数裂变写一个求最大值的函数。

   int Max(int num, ...)    
{        
    va_list arg;        
    va_start(arg, num);       
    int max=arg;        
    while (num--)        
    {             
        int a=(int)va_arg(arg,int);              
        if(max<a)                  
            max=a;        
    }        
    va_end(arg);        
    return max;
}

在函数中有几个定义的宏,让我们具体看一下:

 

1.   va_list  arg;   的意思是定义了一个arg变量,这个arg用于访问参数列表未确定的部分。相当于 一个声明。

 

 

 

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

 

2.   va_start(arg,num)    这个宏的意义是对 arg 进行初始化,其第一个参数是va_list变量的名字arg,第二个参数是省略号前最后有明确参数的变量。在初始化的过程,arg被设置为指向可变参数部分第一个参数

 

 

 

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

 

3.   va_arg(arg,int)这个宏的意思时按第二个参数类型读取可变参数列表。va_list定义的变量arg和列表中下一个参数的类型。特别注意va_arg这个宏会返回这个参数的值,并且指向下一个可变参数。

 

 

 

#define va_end(ap) ( ap = (va_list)0 )

 

4.   va_end(arg)宏的意思是使指针置为无效,相当于p=nuLL.

 

别忘了还要引用对应的<stdarg.h>头文件

 

 

 

 

 

通过如上观察可变参数的函数,我们发现可变参数列表是有局限性的。

 

       1.我们至少必须知道一个明确的参数,当然如果明确参数的有多个,选择最右的已知参数,因为它距离可变参数变量最近啊。如果不懂,回看栈帧。

 

       2.宏是无法判断数量的,所有需要把长度传过去。不然可能不会终止的向后,导致报错。

 

       3.宏是无法判断类型的,你定义已什么类型读,就会已什么类型读取。所以,可变参数函数在调用时非明确的实参应该是同一类型的。

 

了解了可变参数列表可以下一个自己的printf函数了。

 

通过观察库中printf函数。


 

int MyPrint(char * format,...)
{ 
    va_list arg; 
    __crt_va_start(arg, format); 
    while ((*format)!='\0') 
    {  
        if ((*format) == '%')  
        {   
            format++;   
            if ((*format) == 'd')    
                PutInt(__crt_va_arg(arg,int));   
            else if ((*format) == 'c')     
                    putchar(__crt_va_arg(arg, char));    
                 else if ((*format) == 's')    
                        {     
                            char *ap = __crt_va_arg(arg, char *);     
                            assert(ap);     
                            while (*ap != '\0')      
                                putchar(*(ap++));    
                        }  
         }  
        else  
        {   
            putchar(*format);  
        }  format++; 
    } 
    __crt_va_end(arg); 
    return 1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值