让函数能伸能缩
你既需要功能强大如find()
那样可以用函数指针进行搜索的函数,也需要易于使用的函数。printf()
函数有一个很酷的功能:接收参数的数量可变。
你的函数如何做到这点?
这里刚好有个问题需要用到可变数量参数。Head First酒吧里的人正在为计算账单而烦恼,一名员工为了提高工作效率,根据现存鸡尾酒清单创建了一个枚举类型和一个返回每种酒价格的函数:
enum drink {
MUDSLIDE, FUZZY_NAVEL, MONKEY_GLAND, ZOMBIE
};
double price(enum drink d)
{
switch(d) {
case MUDSLIDE:
return 6.79;
case FUZZY_NAVEL:
return 5.31;
case MONKEY_GLAND:
return 4.82;
case ZOMBIE:
return 5.89;
}
return 0;
}
很酷,如果Head First酒吧的员工想知道某种酒的单价,只要调用这个函数就行了。但如果他们想要计算一单酒的总价:
他们希望有一个叫total()
的函数,它接收酒的杯数和这些酒的名字。
可变参数函数
参数数量可变的函数被称为可变参数函数(variadic function)。C标准库中有一组宏(macro)可以帮助你建立自己的可变参数函数。为了弄清它是如何工作的,你将创建一个函数打印一连串
int
的函数:
下面是代码:
我们逐行分析。
百宝箱
函数与宏
宏用来在编译前重写代码,这里的几个宏
va_start
、va_arg
和va_end
看起来很像函数,但实际上隐藏在它们背后的是一些神秘的指令。在编译前,预处理器会根据这些指令在程序中插入巧妙的代码。
这里没有蠢问题
问:等等,为什么
va_end
和va_start
叫宏?它们不就是一般的函数吗?答:不是,它们只是设计成了普通函数的样子,预处理会把它们替换成其他代码。
问:什么是预处理器?
答:预处理器在编译阶段之前运行,它会做很多事情,包括把头文件包含进代码。
问:可以只使用可变参数,而不用普通参数吗?
答:不行,至少需要一个普通参数,只有这样才能把它的名字传给
va_start
。问:如果我从
va_arg
中读取比传给函数更多的参数会怎样?答:会发生不确定的错误。
问:听起来真糟糕。
答:是的,非常糟糕。
问:如果我以
double
或其他类型读取int
参数呢?答:也会发生不确定的错误。
练习
下面轮到你上场了,Head First酒吧的人想要创建一个函数,能够返回一巡酒的总价,函数如下:
使用前几页上的
price()
函数完成total()
函数的代码:
练习解答
下面轮到你上场了,Head First酒吧的人想要创建一个函数,能够返回一巡酒的总价,函数如下:
使用前几页上的
price()
函数完成total()
函数的代码:
试驾
你创建了一些调用函数的测试代码,编译代码并查看结果:
代码正确运行了!
现在你学会了使用可变参数,代码用起来更简单,也更直观了。
要点
接收数量可变参数的函数叫可变参数函数。
为了创建可变参数函数,需要包含stdarg.h头文件。
可变参数将保存在
va_list
中。可以用
va_start()
、va_arg()
和va_end()
控制va_list
。至少需要一个普通参数。
读取参数时不能超过给出的参数个数。
需要知道要读取参数的类型。