可变参数列表解析及模拟实现printf函数
程序员文章站
2022-03-08 15:01:03
...
可变参数列表解析及模拟实现printf函数
我们学过很多函数,他们都有参数,而且参数的个数都是固定的。但是用的时候我们设计函数函数时却血药参数不是固定为好。例如:我们设计一个求平均数的函数,当求两个平均数时我们会传2个参数,三个数的平均数传3个参数………如果要在一个函数中实现n个数的平均值,这里我们就得靠可变参数列表来实现。
下来先看一个简单的例子,使用可变参数,实现函数求未知参数部分n个数的平均值
代码如下:
#include <stdio.h>
#include<stdarg.h>
int average(int n, ...)
{
va_list arg;
int i = 0;
int sum = 0;
va_start(arg, n);
for (i = 0; i < n; i++)
{
sum += va_arg(arg, int);
}
return sum / n;
va_end(arg);
}
int main()
{
printf("%d\n", average(3, 5, 6, 7));
printf("%d\n", average(6, 1, 2, 3, 4,5,9));
return 0;
}
运行结果:
1.va_list定义了一个arg变量,它用于访问参数列表未确定的部分。
2.va_start对其进行初始化,其第一个参数是va_list变量的名字,第二个参数是省略号前第一个有名字的参数。在初始化的过程,arg被设置为指向可变参数部分第一个参数。
3.为了访问访问参数,我们需要使用va_arg。va_arg这个宏接受两个参数,va_list定义的参数和列表中下一个参数的类型。va_arg返回这个参数的值,并且指向下一个可变参数。
4.最后,访问完最后一个参数时,需要调用va_end将指针置为无效。
va_start宏的定义如下
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )**
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
_INTSIZEOF(n)作用:将n的长度化为int长度的整数倍。
va_arg宏定义如下
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
va_arg作用:返回参数的值,并使arg指向下一个参数。
va_end定义如下
#define va_end(ap) ( ap = (va_list)0 )
va_end作用:指针置为无效。
注意:
- 可变参数列表必须按照顺序从头到尾逐个访问,当你在访问几个可变参数后想半途中止,这是可以的。但是如果你一开始就想访问中间的参数,那是不行的。
- 参数列表中至少要有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。
- 这些宏无法直接判断实际存在参数的数量。
- 这些宏无法判断每个参数的类型。
-
如果va_arg中指定了错误的类型,那么后果是不可预测的。
printf函数的实现
代码如下
#include "stdio.h"
#include "stdarg.h"
void show(int n)
{
if (n > 9)
{
show(n / 10);
}
putchar(n % 10 + '0');
}
void print(const char *format, ...)
{
va_list arg;
va_start(arg, format);
while (*format)
{
switch (*format)
{
case 's':
{
char* ret = va_arg(arg, char*);
while (*ret)
{
putchar(*ret);
ret++;
}
break;
}
case 'c':
{
int ch = va_arg(arg, char);
putchar(ch);
}
break;
case 'd':
{
int ret = va_arg(arg, int);
show(ret);
}
break;
default:
putchar(*format);
break;
}
format++;
}
}
int main()
{
print("s ccc d.\n", "hello", 'b', 'i', 't', 100);
return 0;
}
运行结果: