参数个数可变函数的定义和调用

基础知识

  参数个数可变函数是指函数每次被调用时可接收不同个数的实参。在定义和声明一个参数可变的函数时,形式参数的最后必须放置一个省略号。如printf函数的引用性声明为:

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

  其中const char*表示调用函数时必须至少有一个实参,该实参类型为char*;省略号...表示第一个形参之后的参数都是无名参数,无名参数的数量和类型时可变的。

  头文件stdarg.h定义了数据类型va_list,该类型的数据对象用于存放省略号部分的参数。va_start是一个宏定义,用来把(…)部分的参数复制到程序中定义的va_list对象中。va_arg也是一个宏定义,用于访问(…)部分的参数。第一次调用访问(…)部分的第一个参数,第二次调用访问(…)部分第二个参数,以此类推。

  VA_LIST的使用: 
  (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针; 
  (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量; 
  (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数); 
  (4)最后用VA_END宏结束可变参数的获取。

  VA_LIST的实现(以VC6.0_x86为例):

typedef char * va_list;     // TC中定义为void*
#define _INTSIZEOF(n)     ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统
#define va_start(ap,v)    ( ap = (va_list)&v + _INTSIZEOF(v) )     //ap指向第一个变参的位置,即将第一个变参的地址赋予ap
#define va_arg(ap,t)      ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )   //获取变参的具体内容,t为变参的类型,如有多个参数,则通过移动ap的指针来获得变参的地址,从而获得内容
#define va_end(ap)      ( ap = (va_list)0 )   //清空va_list,即结束变参的获取

 

程序实现

例1 average函数计算并返回形参接收的n个浮点数的平均值,n的值由第一个形参接收。

#include<stdio.h>
#include<stdarg.h>
double average(int i,...);
int main()
{
    double j;
    j=average(2,1.5,2.5);
    printf("\naverage=%f\n",j);
    j=average(4,1.0,2.0,3.0,4.0);
    printf("\naverage=%f\n",j);
}
double average(int n,...)
{
    int i;
    double y=0;
    va_list ap;
    va_start(ap,n);
    for(i=0;i<n;i++)
        y+=va_arg(ap,double);
    return y/n;
}

 

#include <stdarg.h> 
 
int AveInt(int,...);
 
 void main()
{
   printf("%d/t",AveInt(2,2,3));
   printf("%d/t",AveInt(4,2,4,6,8));
 
   return;
}
 
int AveInt(int v,...)
{
   int ReturnValue=0;
   int i=v;
 
   va_list ap ;
   va_start(ap,v);
 
   while(i>0)
   {
       ReturnValue+=va_arg(ap,int) ;
       i--;
   }
   va_end(ap); 
   return ReturnValue/=v;
}

例2 模仿实现printf函数。

  输出格式数据:

#include<stdio.h>
#include<stdarg.h>
int mprintf(const char *format, ...);

int main()
{
    mprintf("%d %f %c", 1, 2.3, 'a');
    return 0;
}
int mprintf(const char*format,...)
{
    int i, cnt = 0;
    double d;
    char c;
    char *p=NULL;
    va_list ap;
    va_start(ap, format);
    for (p = format; *p;p++){
        if(*p!='%'){
            putchar(*p);
            continue;
        }
        switch(*++p){
            case 'd':
                i = va_arg(ap,int);
                printf("%d", i);
                cnt++;
                break;
            case 'f':
                d = va_arg(ap,double);
                printf("%f", d);
                cnt++;
                break;
            case 'c':
                c = va_arg(ap,int);
                printf("%c", c);
                cnt++;
        }
    }
    va_end(ap);
    return cnt;
}

  输出字符串:

#include<stdio.h>
#include<stdarg.h>
int my_printf(char* str,...)
{
    va_list ap;//定义char* 变量 ap
    int count = 0;
    char* str_tmp = NULL;
    va_start(ap,str);//为ap进行初始化
    while(*str != '\0')
    {   
        switch(*str)
        {
            case 'c':
                putchar(va_arg(ap,int));//取下一个参数的字符,并打印
                count++;
                break;
            case 's':   
                str_tmp = (char*)va_arg(ap,int);//取下一个参数的地址,因为这个是字符串
                while(*str_tmp != '\0')//利用解引用进行输出
                {       
                    putchar(*str_tmp);
                    count++;
                    str_tmp++;  
                }
                break;
            default:
                putchar(*str);  //不为'c'或's',那么直接将它打印
                count++;
                break;  
        }
        str++;      
    }
    va_end(ap);//将ap指向空,防止野指针
    return count;
}
int main()
{   
    my_printf("s ccccc","hello",'w','o','r','l','d');
    return 0;
}

 

posted @ 2021-05-17 16:11  结城梨斗  阅读(423)  评论(0)    收藏  举报