C 中变参函数的处理方式
C 函数中变化的参数用‘...’ 表示。变化的参数依旧按照C函数传参的规则入栈,即从右往左依次入栈,保证参数从左往右地址依次升高。
解析变参的主要思想是:将变参缓冲区像容纳了不同类型的数组(当然实际的数组里的变量类型不可能是不同的)一样对待。获取变参缓冲区首地址,按已知类型进行强转取值,跳过该值,取出下一个值,取完为止。
但是这里有涉及几个问题:
1,如何知道变参缓冲区的首地址?
2,强转时如何知道该参数的类型?
3,取出该值后,相应的游标应该偏移多少才能指向下一个参数首地址?
4,何时结束解析过程,即如何知道变参的个数?
下面依次解答上面的问题:
首先先看一段代码(摘自微软vadefs.h):
#elif defined(_M_IX86) #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define _crt_va_end(ap) ( ap = (va_list)0 )
...
typedef char * va_list;
#ifdef __cplusplus
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#else
#define _ADDRESSOF(v) ( &(v) )
#endif
...
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
首先应该明确栈中数据成员均按int型字节对齐。栈中的寻址均按int型字节进行。有了这个约定我们就能解决上面4个问题中最关键的两个问题1,3。
代码中 _INTSIZEOF(n) 计算出n所占字节是int字节数的倍数(向上取整)。
_crt_va_start(a,v) 给出变参缓冲区的首地址,等于与第一个变参左侧第一个形参的地址加上该形参字节对齐后应占的字节数。
_crt_va_arg(ap,t) 从宏可以看出t应该是一种类型。将ap所指地址强转成t * 然后取出该值,并将ap偏移到下一个参数首地址处。
相信看到这里,已经知道该怎么变参了。
至于如何知道参数个数,和参数类型,目前只能传参来解决,比如printf和scanf通过%来判断个数,通过%s,%d,%f等中s,d,f来区分类型。