在C语言变参函数中总是会用到下面几个宏:
0、#define _ADNBND (sizeof (acpi_native_int) - 1)
C语言传递参数时是从右到左将参数逐个压栈,因此C语言里通过栈指针来访问参数。C语言在压参数入栈时是”机器字“的大小为最小单位的,也就是说参数的地址都是”字“对齐的。
1、分析 #define _bnd(X, bnd) :
将该宏定义展开为如下形式:
0、#define _ADNBND (sizeof (acpi_native_int) - 1)
1、#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
2、#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
3、#define va_end(ap) (void) 0
4、#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
C语言传递参数时是从右到左将参数逐个压栈,因此C语言里通过栈指针来访问参数。C语言在压参数入栈时是”机器字“的大小为最小单位的,也就是说参数的地址都是”字“对齐的。
1、分析 #define _bnd(X, bnd) :
将该宏定义展开为如下形式:
(
(
(sizeof (X)) + (bnd)
)
&
(
~(bnd)
)
)
bnd一般取3(32位cpu)或7(64位cpu) 。以32位cpu为例: ~(bnd) = 0xfffffffc,该值相当于一个掩码,使任何数与它相与后的结果都是4的倍数,这也正好吻合32位cpu入栈时4字节对齐的特点; (sizeof (X)) + (bnd) = sizeof(X) + 3,保证了不管参数X实际占据几个字节,表达式除以4取整后的值总是大于等于参数X的实际大小。
2、分析 #define va_arg(ap, T):
将该宏定义展开为如下形式:
该宏的功能是:获得ap指向参数的值,并使ap指向下一个参数,T用来指明当前参数类型。
注意((ap) += (_bnd (T, _AUPBND))) 是被一对括号括起来的,然后才减去(_bnd (T, _ADNBND), 所以整个宏取得的值是ap当前指向的参数值,但是先给ap加了当前参数在字对齐后所占的字节数,使其指向了下一个参数。
3、分析 #define va_end(ap)
(void) 0
可能是为了跟 va_start(ap, A) 成对出现,也有可能是为了保证函数返回时能正确的恢复栈。(有疑问?)
4、分析#define va_start(ap, A)
bnd一般取3(32位cpu)或7(64位cpu) 。以32位cpu为例: ~(bnd) = 0xfffffffc,该值相当于一个掩码,使任何数与它相与后的结果都是4的倍数,这也正好吻合32位cpu入栈时4字节对齐的特点; (sizeof (X)) + (bnd) = sizeof(X) + 3,保证了不管参数X实际占据几个字节,表达式除以4取整后的值总是大于等于参数X的实际大小。
2、分析 #define va_arg(ap, T):
将该宏定义展开为如下形式:
(
*(T *)
(
(
(ap) += (_bnd (T, _AUPBND))
)
-
(
_bnd (T,_ADNBND)
)
)
)
该宏的功能是:获得ap指向参数的值,并使ap指向下一个参数,T用来指明当前参数类型。
注意((ap) += (_bnd (T, _AUPBND))) 是被一对括号括起来的,然后才减去(_bnd (T, _ADNBND), 所以整个宏取得的值是ap当前指向的参数值,但是先给ap加了当前参数在字对齐后所占的字节数,使其指向了下一个参数。
3、分析 #define va_end(ap)
(void) 0
可能是为了跟 va_start(ap, A) 成对出现,也有可能是为了保证函数返回时能正确的恢复栈。(有疑问?)
4、分析#define va_start(ap, A)
将该宏定义展开为如下形式:
(void)
(
(ap) =
(
(
(char *) &(A)
)
+
(
_bnd (A,_AUPBND)
)
)
)
该宏的功能是:初始化参数指针ap,将函数参数A右边第一个可变参数的地址赋给ap。 A必须是一个参数的指针,所以可变参数类型函数至少要有一个普通的参数。