<h6>首先看一下代码:</h6>
static inline void MPLog(NSString *format, ...) {
__block va_list arg_list;
va_start (arg_list, format);
NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
va_end(arg_list);
NSLog(@"[Mixpanel] %@", formattedString);
}
#ifdef MIXPANEL_DEBUG
#define MixpanelDebug(...) MPLog(__VA_ARGS__)
#else
#define MixpanelDebug(...)
#endif
<h6>重点关注的是MPLog的实现,我们可以分以下两点解剖该函数:</h6>
1、static inline
inline函数有点类似于宏,内联函数代码会被直接嵌入到被调用的地方,调用几次就会嵌入几次。这样省去了函数调用时的一些额外开销,例如保存和恢复函数返回地址等等。这样加快了速度,但调用次数多的话,会导致可执行文件变大。为了避免这一弱点,内联函数一般都会和static一起出现,从而避免被其它编译单元调用。
2、va_start、va_start
在C语言中,我们经常会见到这样的传参方式:
void func(part_list, ...);
函数参数是以栈的形式存取的,从右至左入栈。参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈,这样栈底是高地址栈顶是低地址。例如:void func(int x, float y, char z);函数调用的时候,进栈顺序是z->y->x,内存中变量存放次序是x->y->z。因此,我们只要知道一个变量的地址和其他变量的类型,通过指针移位运算就可以顺藤摸瓜找到其它的参数,例如:
void fun(int a, ...)
{
int *temp = &a;
temp++;
for (int i = 0; i < a; ++i)
{
cout << *temp << endl;
temp++;
}
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
fun(4, a, b, c, d);
system("pause");
return 0;
}
Output::
1
2
3
4
对于获取省略号指定的参数,在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end来结束,例如:
/*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
int demo( char, ... );
void main( void )
{
demo("DEMO", "This", "is", "a", "demo!", "");
}
/*括号内的省略号表示可选参数*/
int demo( char msg, ... )
{
/*定义保存函数参数的结构*/
va_list argp;
int argno = 0;
char para;
/*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/
va_start( argp, msg );
while (1)
{
para = va_arg( argp, char);
if ( strcmp( para, "") == 0 )
break;
printf("Parameter #%d is: %s\n", argno, para);
argno++;
}
va_end( argp );
/*将argp置为NULL*/
return 0;
}