在学习可变参数时遇到宏
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
目的是将求出n对齐后的字节数,从这里来看应该是按照4字节的整数倍来对齐,为什么这个宏可以实现4字节的整数倍呢?
首先 (sizeof(n)+sizeof(int)-1) 在 sizeof(int) 时相当于( sizeof(n)+3 ) 如果 用 ( sizeof(n)+3 )/4*4, 则结果为4字节的整数倍。
所以 ( sizeof(n)+3 )&~(sizeof(int) - 1) 应该相当于 ( sizeof(n)+3 )/4*4, 这是为什么呢?
~(sizeof(int) - 1) 就是 ~(4 - 1) = = ~(3) , 二进制 11111100
令x=n/4, y=n%4, 则 n=n/4*4+y, n/4*4是对齐后的字节数,而 m & 11111100刚好将后两位,即余数部分置为了0,保留的刚好是n/4*4部分。
所以
n & ~(sizeof(int) - 1) ) 的作用是去掉n对sizeof(int)的余数部分
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) 的在作用是对n的字节数按照 sizeof(int) 的整数倍进行对齐(不足需要补充)。
这个宏的设计很巧妙,实际少是用位运算替代了除法操作,从而提高计算效率。
那么为什么要对齐呢?
因为在函数调用时,参数是通过堆栈进行调用的,估计系统为了便于管理堆栈和参数,将压入堆栈的参数的大小进行了固化,其实字节对齐在其他地方也经常使用,如结构体和类的大小等。sizeof() 获得的仅仅是逻辑长度,并不是真实的物理长度?
另外三个和可变参数相关的宏。
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
v是第一个固定参数, ap 用于指向其他的可变参数, 这里 ap = (va_list)&v + _INTSIZEOF(v) 使得ap指向第一个固定参数后面的第一个可变参数(压入堆栈时,先压入固定参数,再依次压入可变参数,具体方式和硬件和系统有关),如printf(char *pformat, ....)
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
实现将ap指向的参数取出赋给t,并且让p指向下一个可变参数 具体是先(ap += _INTSIZEOF(t)) 之后用 (*( t *)(ap-_INTSIZEOF(t))),设计的也很巧
#define va_end(ap) ( ap = (va_list)0 )
最后将ap指针设置为空。
typedef char * va_list; va_list 就是char *类型的指针。
这些宏在stdarg.h头文件中。