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

例如大家熟知的函数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);

 

 posted on 2014-12-08 23:34  ~疯子~  阅读(1893)  评论(0编辑  收藏  举报