翻看Masonry的源码, 跟踪 #define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
的时候看到有这样的一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| /** * Given a scalar or struct value, wraps it in NSValue * Based on EXPObjectify: https://github.com/specta/expecta */ static inline id _MASBoxValue(const char *type, ...) { va_list v; va_start(v, type); id obj = nil; if (strcmp(type, @encode(id)) == 0) { id actual = va_arg(v, id); obj = actual; } else if (strcmp(type, @encode(CGPoint)) == 0) { CGPoint actual = (CGPoint)va_arg(v, CGPoint); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(CGSize)) == 0) { CGSize actual = (CGSize)va_arg(v, CGSize); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(MASEdgeInsets)) == 0) { MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets); obj = [NSValue value:&actual withObjCType:type]; } else if (strcmp(type, @encode(double)) == 0) { double actual = (double)va_arg(v, double); obj = [NSNumber numberWithDouble:actual]; } else if (strcmp(type, @encode(float)) == 0) { float actual = (float)va_arg(v, double); obj = [NSNumber numberWithFloat:actual]; } else if (strcmp(type, @encode(int)) == 0) { int actual = (int)va_arg(v, int); obj = [NSNumber numberWithInt:actual]; } else if (strcmp(type, @encode(long)) == 0) { long actual = (long)va_arg(v, long); obj = [NSNumber numberWithLong:actual]; } else if (strcmp(type, @encode(long long)) == 0) { long long actual = (long long)va_arg(v, long long); obj = [NSNumber numberWithLongLong:actual]; } else if (strcmp(type, @encode(short)) == 0) { short actual = (short)va_arg(v, int); obj = [NSNumber numberWithShort:actual]; } else if (strcmp(type, @encode(char)) == 0) { char actual = (char)va_arg(v, int); obj = [NSNumber numberWithChar:actual]; } else if (strcmp(type, @encode(bool)) == 0) { bool actual = (bool)va_arg(v, int); obj = [NSNumber numberWithBool:actual]; } else if (strcmp(type, @encode(unsigned char)) == 0) { unsigned char actual = (unsigned char)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedChar:actual]; } else if (strcmp(type, @encode(unsigned int)) == 0) { unsigned int actual = (unsigned int)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedInt:actual]; } else if (strcmp(type, @encode(unsigned long)) == 0) { unsigned long actual = (unsigned long)va_arg(v, unsigned long); obj = [NSNumber numberWithUnsignedLong:actual]; } else if (strcmp(type, @encode(unsigned long long)) == 0) { unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long); obj = [NSNumber numberWithUnsignedLongLong:actual]; } else if (strcmp(type, @encode(unsigned short)) == 0) { unsigned short actual = (unsigned short)va_arg(v, unsigned int); obj = [NSNumber numberWithUnsignedShort:actual]; } va_end(v); return obj; }
#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
|
static inline id _MASBoxValue(const char *type, ...)
是个可变参数的内敛函数。
可变参数函数
可变参数是C语言的一个特性。va_XXX 这些函数操作的原理是:在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码,这个入栈过程,堆栈地址不断递减,一些黑客就是在堆栈中修改函数返回地址,执行自己的代码来达到执行自己插入的代码段的目的. 总而言之栈中的地址分布是这样的, 地址从高到低,依次是:函数参数列表,函数返回地址,被调用函数局部变量. 另外:最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分
计算Int内存占用大小的宏,#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
1 2 3 4 5 6 7 8
| #ifdef _M_ALPHA typedef struct { char *a0; /* pointer to first homed integer argument */ int offset; /* byte offset of next parameter */ } va_list; #else typedef char * va_list; #endif
|
1
| #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //得到第一个可变参数地址,
|
1
| #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数地址
|
1
| #define va_end(ap) ( ap = (va_list)0 ) // 将指针置为无效
|
虽然可以通过在堆栈中遍历参数列表来读出所有的可变参数,但是由于不知道可变参数有多少个,什么时候应该结束遍历,如果在堆栈中遍历太多,那么很可能读取一些无效的数据.
解决办法:可以在第一个起始参数中指定参数个数,那么就可以在循环还中读取所有的可变参数;b.定义一个结束标记,在调用函数的时候,在最后一个参数中传递这个标记,这样在遍历可变参数的时候,可以根据这个标记结束可变参数的遍历;
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <stdio.h> #include <stdarg.h> #include <stdlib.h> //第一个参数指定了参数的个数 int sum(int number,...) { va_list vaptr; int i; int sum = 0; va_start(vaptr,number); for(i=0; i<number;i++) { sum += va_arg(vaptr,int); } va_end(vaptr); return sum; } int main() { printf("%d\n",sum(4,4,3,2,1)); system("pause"); return 0; }
|
var_XXX 宏的理解
宏:#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
。
先看看 ~(sizeof(int) - 1)
的值, sizeof(int)
为4,减1为3,二进制表示为 11
,取非,则变成为 00
。 ((sizeof(n)+sizeof(int)-1)&
与上 00
。 也就是说 _INTSIZEOF(n)
计算出来的值肯定是 4的倍数。 也就是说当sizeof(n)的结果在1~4之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在5~8时,_INTSIZEOF(n)的结果会是8...
宏:#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
va_list(第一个参数的最后一个字节的地址,也就是函数返回指针的地址减一)
可看成是 char * 类型
,进行这样的指针转换是为了让 v地址+1的时候,地址指针就是+1,而不是+4(指针加法)。
宏:( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
(ap += _INTSIZEOF(t)
使ap指向下一个参数的地址。 - _INTSIZEOF(t)
后再 *(t *)
获取 t类型的值