首页 > 代码库 > 可变参数列表

可变参数列表

  可变参数是指某一个函数被调用的时候,并不知道具体传递进来的参数类型和参数的数目,

例如大家熟知的函数printf()。C语言是通过软件堆栈的方式进行参数传递的,对于下面的函

数,从左到右依次压入栈中的变量为:a、b、c,如果存在更多的参数,只要在函数真正被调用

前按照同样的顺序依次压入栈中就可以完成任意数量的参数传递,这就是可变参数传递的原理。

在函数声明时,在函数列表最右边加入一个省略号“...”作为参数就可以将一个函数声明为可变

参数传递,例如:

1 /*一个使用可变参数的例子*/2 void printf(char *pString, ...); 

 

  可变参数实际上具有参数类型 va_list.在参数内部必须要首先声明一个可变参数变量,以便

依次取出所有传入的数据,例如:

1 va_list    Example;        /*定义一个可变参数列表*/

 

  va_list 可以像普通变量一样充当函数的参数和返回值,例如

1 /*定义一个函数,需要上级函数传递一个va_list型变量的指针*/2  void FuncExample(va_list *pva);

 

   我们可以通过宏va_start()告知函数准备从堆栈中取数据。其中,使用va_start()需要传递

两个参数,分别是va_list变量以及函数参数列表中“...”左边的第一个形参的名称,例如:

1 va_start(Example,pString);        /*告知函数准备从可变参数列表Example中取数据*/

 

  与va_start()对应,我们可以通过宏va_end()告知函数不再继续进行参数的提取,例如;

1 va_end(Example);        /*结束参数提取*/

 

  在va_start()和va_end()所划定的范围内,我们可以通过va_art()依次提取所需要的参数,

其中提取参数的顺序和调用时传递的参数顺序相同,例如:

1 uinsigned int A = va_arg(Example,unsigned int);     /*提取一个unsigned int 型的数据*/

 

也可以通过va_copy作为当前的参数列表做一个备份(备份当前的参数读取位置),例如:

1 /*保存当前的参数栈*/2 va_list ExampleB;                       /*定义一个新的可变参数列表*/3 va_copy(ExampleB,Example);    /*复制当前的参数栈信息到ExampleB*/

 



 

综合演示

 

  该范例用于实现向指定的设备输出的可变数量的字符串。我们首先需要利用函数指针构造一个

输出设备驱动函数表,将所有的输出设备以数组的形式组织在一起: 

 1 /*定义输出设备的驱动函数原型*/ 2 typedef void OUTPUT_DRV(unsigned char *pstr,va_list *pArg);  3 /*注意这里不是定义函数指针,而是定义了一个函数原型*/ 4  5 OUTPUT_DRV LCD_Drv;     /*定义了一个函数LCD_Drv()*/ 6 OUTPUT_DRV PRN_Drv;     /*定义了一个函数PRN_Drv()*/ 7  8 /*定义指向OUTPUT_DRV类型函数的函数指针*/ 9 typedef OUTPUT_DRV *P_DRV;10 11 /*使用函数指针构造一个驱动函数表*/12 P_DRV OutputDrivers[] = {&LCD_Drv,&PRN_Drv};

 

  接下来我们将通过可变参数实现一个向指定设备输出类似printf格式字符串的函数,具体的设备

需要用户通过字符串的形式给出,例如“LCD”或者“PRN”。该函数将根据用户输入的字符串决定输出

的设备和字符串:

 1 #include <stdarg.h> 2 #include <string.h> 3  4 /*定义输出设备驱动函数的原型*/ 5 int Print(unsigned char *DrvNAME,...) 6 { 7     unsigned char *pstr = NULL; 8     P_DRV fnDrv = NULL; 9     va_list Arg;        /*定义可变参数列表*/10     11     if(NULL == DrvNAME) {12         return -1;13     }14     15     /*确定使用哪个设备进行输出*/16     if(strcmp(DrvNAME,"LCD") == 0) {17         fnDrv = OutputDrivers[0];   /*使用LCD驱动*/18     } else if(strcmp(DrvNAME,"PRN") == 0) {19         fnDrv = OutputDrivers[1];   /*使用打印机驱动*/20     } else {21         return -1;22     }23     24     va_start(Arg,DrvNAME);              /*开始取参数*/25     pstr = va_arg(Arg,unsigned char *)  /*获取一个字符串*/26     fnDrv(pstr,&Arg);                   /*调用指定的设备驱动*/27     va_end(Arg);                        /*结束取参数*/28 }
 1 /*驱动函数实体*/ 2 void LCD_Drv(unsigned char *pstr,va_list *pArg) 3 { 4     ... 5     /*  在函数中可以通过 va_arg(*pArg,类型)来依次提取参数,不需要 6         通过va_end(*pArg)来标注取参数结束,如果通过va_copy生成了一 7         个新的va_list变量,则需要在取出参数后通过va_end()将该变量 8         关闭 **/ 9     ...10 }11 12 /**Print()的操作范例*/13 unsigned char Day = 3;14 Print("LCD","Is‘s the %dth day of this week.\n",Day);

 

可变参数列表