不定参数在一些特殊场合下是很有用的,例如在字符串的格式化合成,像printf()函数、日志输出等场合都很容易想到这样的结构。
其实,这种方法在C语言里就已经存在了,也不是什么新奇特的东西,一般要实现这样的功能,需要以下几个函数/类型组成:
va_list 、va_start、va_avg、va_end
头文件:stdarg.h
先来看两个例子:
例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
例2:
double sum_series(int num, ...) { double sum = 0.0, t; va_list argptr; va_start(argptr, num);//初始化变元指针 while (num--) { t = va_arg(argptr, double); sum = sum + t; } va_end(argptr); return sum; }
PS:以上两个例子是摘自网上,我想比较简单,大家百度一下也是一大堆。
先来说说,这两种方式的缺点吧:
例1,需要输入一个“0”作为结束符,而且中间参数中不能出现0;这个条件限制,另外输入的参数必须是同一种类型,不是非常灵活。
例2,需要预先输入参数的个数,这里也有严重缺陷的,因为如果输出的num大于实际后面的参数个数的话,就越界了;另外参数增加就要修改num的输入值,这样也不方便;输入的参数必须是同一种类型,不是非常灵活。
问题,printf可是没有要求输入参数的个数以及类型必须统一,这样的要求哦?
的确,printf很灵活,没有那么限制,那么如何实现呢?难道va_arg存在特殊判断可以确定最后一个参数?
遗憾的是,va_arg是不知道最后一个参数的,除非像例子里一样,告之个数或特殊参数为结尾。。。
那么printf怎么做到的?
猜测,printf的第一个参数为 char* ,是个字符串,而且输入的时候,有参数的时候必须以“%”为开头,
那么,问题就简单了,我们先遍历一遍,看看%出现几次(排除%%),不就是知道了个数了。
然后在解析下%后面的s d什么的,就不是问题了。。。。
最后再给个例子
void DEBUG_LOG(uint nLogID,char *strFmt,...) { char strLog[2048]; //查找“%” va_list lsID; va_start(lsID, strFmt); vsprintf(strLog, strFmt, lsID); va_end(lsID); DEBUG_LOG00(nLogID,strLog); }