首先,看宏定义
typedef char *va_list;
//va_list ap;
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) //类型检查, 大小
//T 为 type, 数据类型
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
//把列表中的参数,转换为对应类型的指针,然后取这个地址里的内容
#define va_end(ap) (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
//取A(最后一个参数)地址,强转为char *
//+ A 的类型大小
va_list 是一个char *类型的指针,指向参数列表的参数。
bnd宏, 作用是类型检查, 在+3 然后又 & ~3之后实际已经是0了, 应该是内核为了安全做的,这里主要是sizeof(X)。
按位&, 目的就是清后2位。 宏定义 _AUPBND = sizeof(u32) - 1 = 3; 这样如果是char那么占一个字节,+3 =4 再 &~3 ,bit2还是1, 所以bit2 起码都得是1,但是这样char岂不是和int一样了?那绝对不行,所以在处理的时候,va_arg 会有类型转换。
va_start(ap, A)宏,ap是一个 va_list 型变量, A是最后一个参数的名称。作用:初始化变量参数列表。A是调用函数知道其类型的最后一个参数,取了地址,然后用char *强转,因为什么指针都只占4个字节,它的作用就是指向参数的地址 然后把这个地址让ap去指向, ap是一个va_list* 变量,本质也是char* ,这样才没毛病。 注意这个参数不应声明为寄存器变量。
va_end(ap)宏,是个0, 只是为了释放ap这个指针。
va_arg(ap, T)宏,要在va_start后使用。获取参数列表的下一个参数。
(*(T *)( ((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)) ) );
这里是比较难理解的, 简化一下, 假设T = int, 则 *(int *)((ap += 4) - 4);
ap = ap+4, 此时ap指向的地址向后移动了4个字节,比如原来指向0x11,现在指向0x15, 然后 ap-4 又回到了原来的地址,
但是指针已经不是那个指针了。 为了要+4呢, 因为变量是在栈里的,在入栈。 红线代表回到原来地址。
va_arg()宏扩展为具有调用中下一个参数的类型和值的表达式。每次调用va_arg()都会修改ap,以便下一次调用返回下一个参数,其中ap是va_start宏初始化的。参数T是一个指定的类型名,是一个指向有明确类型对象的指针的type(the type of a pointer to an object that has the specified type ),因此可以通过像类型加*获得。
应用举例1,注意char的转换:
void foo(char *fmt, ...)
{
va_list ap;
int d ;
char c, *s;
va_start(ap, fmt);
while(*fmt)
{
switch(*fmt++){
case 'S':
case 's':
s = va_arg(ap, char*);
printf("string is %s\n", s);
break;
case 'd':
d = va_arg(ap, int);
printf("type int data is %d\n", d);
break;
case 'c':
c = (char)va_arg(ap, int);
printf("type char data is %c\n", c);
break;
}
va_end(ap);
}
}
void main()
{
int var = 300;
char *buf = "aaaffff";
char c = 'a' ;
foo("%s,%S, %d, %c\n",buf, buf, var, c );
}
输出:
string is aaaffff
string is aaaffff
type int data is 300
type char data is a
应用举例2,内核源码:
static int cx23885_api_cmd(struct cx23885_dev *dev,
u32 command,
u32 inputcnt,
u32 outputcnt,
...)
{
u32 data[CX2341X_MBOX_MAX_DATA];
va_list vargs;
int i, err;
dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
va_start(vargs, outputcnt);
for (i = 0; i < inputcnt; i++)
data[i] = va_arg(vargs, int);
err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data);
for (i = 0; i < outputcnt; i++) {
int *vptr = va_arg(vargs, int *);
*vptr = data[i];
}
va_end(vargs);
return err;
}
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{
va_list args;
int len;
if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
return -ENOMEM;
}
va_start(args, format);
len = vsnprintf(&env->buf[env->buflen],
sizeof(env->buf) - env->buflen,
format, args);
va_end(args);
if (len >= (sizeof(env->buf) - env->buflen)) {
WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
return -ENOMEM;
}
env->envp[env->envp_idx++] = &env->buf[env->buflen];
env->buflen += len + 1;
return 0;
}