va_list和va_start和((A*)0)->a
C语言函数是从右到左入栈的
va_list ap;//=char *ap;(一个字符指针)
va_start(ap,v) 中 ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )//返回参数v后的参数列表地址(V地址+V的长度)
va_arg(ag,type):va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )//取指定类型的值int i=va_arg(ap,int),并且使ap指向向一个参数
va_end(ag):清空参数列表,并置参数指针ag无效。说明:指针ag被置无效后,可以通过调用 va_start()、va_arg恢复arg。每次调用va_start() / va_arg()后,必须得有相应的va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内
注:
其中:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1)
~是位取反的意思。_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。((5+4-1)/4)*4=8;
~(sizeof(int) – 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) – 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) – 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) – 1) )后就正好将原长度补齐到4的倍数了
即:
((5+4-1)/4)
(sizeof(n) + sizeof(int) – 1)&~(sizeof(int-1)
栈底 高地址
| .......
| 函数返回地址
| .......
| 函数最后一个参数
| ....
| 函数第一个可变参数 <--va_start后ap指向
| 函数最后一个固定参数
| 函数第一个固定参数
栈顶 低地址
Linux内核中,用两个非常巧妙地宏实现了,一个是offsetof宏,另一个是container_of宏,下面讲解一下这两个宏。