函数的可变参数

一般而言,在设计函数时会遇到许多数学和逻辑操作,是需要一些可变功能。例如,计算数字串的总和、字符串的联接或其它操作过程。

实现一个函数,要求在函数中计算传入的所有参数之和,并输出到屏幕上。这个函数实现起来并不困难,问题在于这个函数的参数个数是不确定的:假设这个函数的名字是sum_n,那么程序员既可以调用sum_n(1, 2)来计算两个数的和,又可以调用sum_n(2, 3, 4)来计算三个数的和,还可以调用sum_n(1, 5, 8, 9)来计算四个数的和。

要实现这个函数,显然不能为不同数量的参数实现不同的函数——这些函数完成的功能完全相同,只是提供的参数不同而已,都实现一遍太浪费时间了。这时就需要创建参数个数不确定的函数来解决这个问题。

 

C语言中允许定义一个具有不确定个数参数的函数,这种情形被称为可变参数,也叫不定参数。带有可变参数的函数的声明方式如下:

  1. 返回值类型函数名(形式参数列表, ...)  

与固定参数的函数相比,可变参数的函数在声明时只要在形参列表的最后提供额外的三个“.”即可。可变参数的函数仍然可以有个数确定的固定参数,固定参数之后则是个数可变的可选参数。

下面就是带有一个固定参数和可选参数的函数声明:

  1. int func_a(int x, …)  

下面则是一个带有两个固定参数和可选参数的函数声明:
  1. int func_b(char a, double b, …)  

完成了可变参数函数的声明,下面来看看如何在对应的函数中得到传递进来的实际参数——肯定不能靠省略号“…”来访问可选参数。

要处理可变参数,需要用C到标准库的va_list类型和va_start、va_arg、va_end宏,这些定义在stdarg.h头文件中。

首先需要使用va_list定义一个变量,这个变量将被用来存储指向不同可变参数的指针。有关指针的概念将在“指针”一章中详述,这里大家只要了解如何使用即可。下面的语句定义了一个名为argPtr的可变参数指针。

  1. va_list argPtr;  

刚刚定义的argPtr没有任何意义,因为还没有进行初始化。宏va_start用来初始化argPtr,得到的是第一个可变参数的地址。
  1. va_start(argPtr, <最后一个固定参数>);  

例如对于下面的函数声明
  1. int func_b(char a, double b, …)  

应当使用

  1. va_start(argPtr, b);  

对argPtr进行初始化。初始化后,argPtr指向第一个可变参数(注意不是指向最后一个固定参数)。

宏va_arg可以返回argPtr当前指向的可变参数的值,同时修改argPtr,使其指向下一个可变参数。调用va_arg时还要指定当前参数的类型。

下面的语句将取得argPtr指向的可变参数(类型为double)放到变量val中,并将argPtr指向下一个可变参数的位置:

  1. double val = va_arg(argPtr, double);  

最后需要调用va_end使argPtr无效:
  1. va_end(argPtr);  

注意:宏va_start和宏va_end需要成对出现。

回到开头sum_n的例子上。要计算不确定个数的参数之和,函数sum_n有下面两种实现思路:

1. 将要相加的所有数直接放在参数中,最后使用一个特殊值来标记参数列表的结束,这个值被称为结束符。例如选择-1作为结束符,可以像下面这样调用函数sum_n:

  1. sum_n(1, 2, 3, 5, 10, 28, 4, -1);  

这种方法的问题在于要相加的数中不能有被选为结束符的那个数(例如-1),否则在函数sum_n遍历参数列表时,遇到第一个-1就认为参数列表结束了。

2. 首先传递一个数标明这次调用时一共有几个数需要相加,然后才是所有要想加的数。这样的好处是无需使用特殊的结束符。例如在计算1+2+4时,可以像下面这样调用

  1. sum_n:  
  2. sum_n(3 /* 一共三个数 */, 1, 2, 4);  

这里选择第二种方式来实现函数sum_n。

任意个数的参数相加的实现:

  1. #include <stdio.h>  
  2. #include <stdarg.h>  
  3. /* 至少应该有一个数来相加,first 代表第一个要相加的数 */  
  4. void sum_n(int count, int first, ...)  
  5. {  
  6.          inti;  
  7.          intsum;  
  8.          va_listargPtr;  
  9.          /*检查 count 是否合理 */  
  10.                    if(count< 1)  
  11.                    {  
  12.                             printf("参数个数不合理!\n");  
  13.                             return;  
  14.                    }  
  15.                    /*初始化 sum */  
  16.                    sum= first;  
  17.                    /*初始化 argPtr */  
  18.                    va_start(argPtr,first);  
  19.                    /*由于第一个数已经在 sum 中了,所以 i 从 1 开始计数 */  
  20.                    for(i= 1; i < count; ++i)  
  21.                    {  
  22.                             intval = va_arg(argPtr, int);  
  23.                             sum+= val;  
  24.                    }  
  25.                    va_end(argPtr);  
  26.                    printf("%d个数的和为 %d。\n", count, sum);  
  27.          }  
  28.          intmain()  
  29.          {  
  30.                    sum_n(1,1);  
  31.                   sum_n(2, 3, 4);  
  32.                    sum_n(6,200, 1, 2, 3, 4 ,5);  
  33.                    sum_n(0,1, 2, 3);  
  34.          }  


使用可变参数时需要注意:

使用可变参数的函数必须至少有一个固定参数,定义可变参数的函数时,固定参数必须位于可选参数之前;开发者需要自己确定可选参数的类型;开发者需要自己确定可选参数的数量(例如将可选参数的数量当作一个固定参数传到函数中)。


C语言规定至少要定义一个有名字的参数,因为va_start宏要用到参数列表中最后一个有名字的参数,从它的地址开始找可变参数的位置。实现者应该在文档中说明参数列表必须以NULL结尾,如果调用者不遵守这个约定,实现者是没有办法避免错误的。

另外va_arg(argp, type)宏中不支持的类型有:

  •  char、signed char、unsigned char
  •  short、unsigned short
  •  signed short、short int、signed short int、unsigned short int
  •  float

va_arg宏的第2个参数不能被指定为char、short或者float类型。
因为char和short类型的参数会被转换为int类型,而float类型的参数会被转换为double类型 ……
例如,这样写肯定是错误的:

  1. c = va_arg(ap,char);  

因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。所以应该写成:
c = va_arg(ap,int);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值