c语言中printf 函数不定长参数的实现

printf(const char *fmt, …)是一个可变参数函数, 第一个参数为字符串, 后面是格式化输出参数列表. C语言中函数的参数都是压进栈中的(参数压栈方向是从右往左!), 可变参数的函数必须有一个参数表示参数的个数, 才能让编译器知道压栈多少参数, 以及函数返回时弹出多少个参数, 我们在fmt字符串中提取’%‘的个数, 以及针对’%'后面不同的字符来处理。

1.va_list类型
va_list 的 va 是variable-argument(可变参数). list 就是列表的意思

typedef char *va_list;		//Linux内核中va_list的实现
va_list 	ap;

2.va_start
va_start(ap, A)宏将ap初始化,以便随后被va_arg()和va_end()使用,必须先调用。

#define va_start(ap,fmt) ( ap = (va_list)&fmt + _INTSIZEOF(fmt) )

第一个参数ap, 就是刚刚提到的va_list类型, 第二个参数A, 就是一个确定的类型, 也就是"…"的类型, 他会将这些类型的地址放到va_list中, 根据初始化A来初始化AP.
注意: 传给宏va_start 的参数fmt是可变参数列表中的前一个参数, ap指向变参列表中的第一个参数地址.
函数参数压栈时, 参数的入栈顺序时从右往左, 出栈时是从左往右. 函数调用时, 先把若干个参数都压入栈中, 再压fmt, 最后压pc, 这样一来, 栈顶指针偏移找到了fmt, 通过fmt中的%占位符, 取后面参数的个数, 从而正确获得所有参数.

#define _INTSIZEOF(n)   ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

计算int类型按4字节对齐后的结果. 通过_INTSIZEOF(n), 可以根据一个变量的类型计算变量再内存中占用的字节数, 从而正确定位参数在内存的位置.

3.va_arg
va_arg(ap, T)宏扩展为一个表达式, 该表达式具有调用中下一个参数的类型和值.

va_arg()宏在va_start()宏之后的第一次使用会返回最后的参数. 连续的调用会返回其余参数的值.

#define va_arg(ap, T) 	(*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADN    BND))))// Linux 内核中宏定义原型

#define va_arg(ap,type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )

4.va_end
va_end 是一个宏

#define va_end(ap)	(ap = (va_list) NULL)

#define va_end(ap)    ( ap = (va_list)0 )

通过这个宏, 可以清楚的看到, 将va_list类型的ap赋值为NULL.
示例:

void test(int fmt, ...)
{
	va_list ptr;//相当于char* ptr
	va_start(ptr, fmt);//
	int par1 = va_arg(ptr, int);//par1 = 15
	char par2 = va_arg(ptr, char); //par2 = 'E'
	double para3 = va_arg(ptr, double); //para3 = 3.1415920000000002
	float para4 = va_arg(ptr, float); //para4=2.00000000
	va_end(ptr);//本质是ptr = null
}
int main()
{
	int ret = 1;
	int para1 = 15;
	char para2 = 'E';
	double para3 = 3.141592;
	float para4 = 3.1415926f;
	test(1,para1,para2, para3, para4);
	printf("hello world");
	return 0;
}

5.vsprintf(char *buf, const char *fmt, va_list args) 函数, 将变量列表args中的参数按照fmt中规定的格式保存到临时缓冲buf中.
如果成功,则返回写入的字符总数,否则返回一个负数。

#include <stdio.h>
#include <stdarg.h>

char buffer[80];
int vspfunc(char *format, ...)
{
   va_list aptr;
   int ret;

   va_start(aptr, format);
   ret = vsprintf(buffer, format, aptr);
   va_end(aptr);

   return(ret);
}

int main()
{
   int i = 5;
   float f = 27.0;
   char str[50] = "runoob.com";

   vspfunc("%d %f %s", i, f, str);
   printf("%s\n", buffer);
   
   return(0);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值