在C语言中,通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数(不固定)。
我们来看一个例子,并且通过剖析这个例子来了解可变参数列表
编译器:vs2013
#include <stdio.h>
#include <stdarg.h>
#include <windows.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);
}
va_end(arg);
return sum / n;
}
int main()
{
int avg = average(4, 1, 2, 3, 5);
printf("%d\n", avg);
system("pause");
return 0;
}
首先,我们来分析部分代码
1.
int average(int n, ...) // ... 未知参数列表,n为未知参数列表个数
//将部分参数(1, 2, 3, 5)传给了未知参数列表
2.
代码
va_list arg;
转到定义
typedef char * va_list;
3.
代码
va_start(arg, n);
转到定义
#define va_start _crt_va_start
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _ADDRESSOF(v) ( &(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
4.
代码
va_arg(arg, int);
转到定义
#define va_arg _crt_va_arg
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
5.
代码
va_end(arg);
转到定义
#define va_end _crt_va_end
//宏,_crt_va_end被重命名为 va_end
#define _crt_va_end(ap) ( ap = (va_list)0 )
//宏,( ap = (va_list)0 )
之后,我们对average函数的部分代码进行替换
int average(int n, ...)
{
char *arg;
int i = 0;
int sum = 0;
arg = (char *)(&(n)) + 4;
for (i = 0; i < n; i++)
{
sum += *(int *)((arg += 4) - 4);
}
arg = (char *)0;
return sum / n;
}
综上所述,我们可以了解到
- 声明一个va_list类型的变量,用于访问参数列表的未确定部分。
- 这个变量是用va_start来初始化的,它的第一个参数是va_list的变量名,第二个参数是省略号前最后一个有名字的参数。初始化过程把arg变量设置为指向可变参数部分的第一个参数。
- 为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的类型。在上述的例子中,所有可变参数都是整型。va_arg返回这个参数的值,并使va_arg指向下一个可变参数。
- 最后,我们访问完毕最后一个参数之后,需要调用va_end。
可变参数列表的应用
1.使用可变参数,实现函数,求函数参数的最大值
#include <stdio.h>
#include <stdarg.h>
#include <windows.h>
int Max(int n, ...)
{
va_list arg;
int i;
int max = 0;
va_start(arg, n);
max = va_arg(arg, int);
for (i = 1; i < n; i++)
{
int tmp = va_arg(arg, int);
if (max < tmp)
max = tmp;
}
va_end(arg);
return max;
}
int main()
{
int ret = Max(4, 1, 2, 3, 4);
printf("%d\n", ret);
system("pause");
return 0;
}
模拟实现简单的printf函数
模拟实现简单的printf函数,需要利用传入的第一个参数,对字符串经行遍历,若遇到字符为d,c,s等等(模拟%d,%c,%s),则利用va_age获取并输出后面所对应的未知参数列表中的参数,若没有遇到,则原样输出。
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include <windows.h>
void print(const char *format, ...)
{
char *str = (char *)format;
va_list arg;
va_start(arg, format);
while (*str != '\0')
{
int ch;
char *s;
switch (*str)
{
case 'd':
ch = va_arg(arg, int);
while (ch > 10)
{
int d = 0;
int i = 1;
d = ch / 10;
while (d > 10)
{
d = d / 10;
i++;
}
d = d + 48;
putchar(d);
i = (int)pow(10, i);
ch = ch % i;
}
ch = ch + 48;
putchar(ch);
str++;
break;
case 'c':
putchar(va_arg(arg, char));
str++;
break;
case 's':
s = va_arg(arg, char*);
while (*s != '\0')
{
putchar(*s);
s++;
}
str++;
break;
default:
putchar(*str);
str++;
break;
}
}
}
int main()
{
print("s ccc d.\n", "hello", 'b', 'i', 't', 1234);
system("pause");
return 0;
}