va_start,va_list函数的分析及使用

首先,看宏定义

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;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
va_listva_start函数都是C语言中可变参数的实现机制。 va_list是一个类型,表示一个变长参数的列表。它的定义在<stdarg.h>头文件中。在函数中声明一个va_list类型的变量,用于存储可变参数的列表。 va_start函数是C语言中的宏,用于初始化一个va_list变量。它的原型在<stdarg.h>头文件中定义。va_start接受两个参数,第一个参数是一个va_list类型的变量,第二个参数是可变参数列表的前一个参数。它的作用是将va_list变量指向可变参数列表的起始位置。 使用va_listva_start可以实现在函数中接受任意数量的参数。例如,如果一个函数需要接受可变数量的整数作为参数,我们可以使用va_listva_start来实现: ```c #include <stdarg.h> #include <stdio.h> void printIntegers(int count, ...) { va_list args; va_start(args, count); for (int i = 0; i < count; i++) { int num = va_arg(args, int); printf("%d ", num); } va_end(args); } int main() { printIntegers(3, 1, 2, 3); // 输出:1 2 3 printIntegers(5, 10, 20, 30, 40, 50); // 输出:10 20 30 40 50 return 0; } ``` 上述代码中,printIntegers函数接受一个整数count和任意数量的整数作为参数。通过使用va_listva_start,我们可以在函数中遍历可变参数列表,并打印出所有的整数。 需要注意的是,在使用完可变参数列表后,我们需要调用va_end来结束va_list使用,释放资源。 总之,va_listva_start是C语言中处理可变参数的重要函数和类型,它们使得函数可以接受任意数量的参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值