C语言中变参函数解析

1. 变参函数:

即参数个数、类型皆不定的函数,最常见的如printf()函数;

2. 头文件:

早期Unix System V兼容方式头文件名是<varargs.h>, ANSI标准规范指定头文件名<stdarg.h>, GCC目前已经不再支持 include varargs.h文件

3. 宏定义:

va_list(), va_arg(), va_start(), va_end();

4. 源码示例:

 <linux kernel version 3.0> printk()  源码(删减):
asmlinkage int printk(const char *fmt, ...)
{
	va_list args;
	int r;

	va_start(args, fmt);
	r = vprintk(fmt, args);
	va_end(args);

	return r;
}
其中, ... 表示不定参数。

大致流程为:

(1). 声明 va_list 变量 args,其中,va_list 类型为 char *;

typedef char *va_list;
(2). 调用va_starts(args, fmt)函数,获取fmt参数后面的不定参数中的第一个参数地址;

va_start(args, fmt);
(3). 调用子函数vprintk()

(4). 调用函数va_end(args)使得arg指向NULL

5. 宏定义解析

va_start 的宏定义如下:

#define va_start(ap, A)      (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

其中,_bnd宏定义为:

#define _bnd(X, bnd)         (((sizeof (X)) + (bnd)) & (~(bnd)))

_bnd(X,bnd)的功能是返回X类型按照当前机器对齐后占用空间的大小:按四字节对齐时,_AUPBND的值是3,若X类型的大小小于4,占用空间大小为4,若大于4,按照进位成4的整数倍返回

函数调用时,参数入栈的顺序是从右往左,而栈空间地址是向下延伸的,所以从最左边参数到最右边参数是一块地址连续增长的内存。因此,va_start宏的作用是,使得指针参数ap指向固定参数(char * fmt)后的第一个参数。

指向第一个参数后,可以调用宏va_arg()来获取下一个参数地址,定义如下:

#define va_arg(ap, T)  (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

首先将ap的地址指向下一个参数地址,然后表达式返回第一个参数的值。

va_end宏定义很简单:

#define va_end(ap)          (void) 0
在此版本定义中va_end不作任何操作,一般认为需要将ap指针指向NULL:

#define va_end(ap)       ap=(char*)0

这样在代码中就消除了“野指针”。

6. 例子

功能: 实现一个求若干个数的平方和的函数,至少有一个参数,参数结束标志为一个-1的参数。标志位存在的原因是,变参函数并不能确定何时停止读入函数。

函数代码如下:

int sum_square(int n,...)
{
	int k = 0;
	int temp =0;
	va_list args;

	if(n == -1){
		return 0;
	}

	va_start(args,n);
	k+= n*n;
	while((temp=va_arg(args,int)) != -1){
		k+= temp * temp;	
	}
	va_end(args);
	return k;
}

若函数调用没有-1参数,则会出现难以预料的结果,一般是由于访问非法内存产生的“段错误”。

printf("sum of square is %d \n",sum_square(1,2,3,4,-1));
运行后显示:

sum of square is 30










  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值